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
442{
443 if (!is_receiving_.load())
444 {
445 return;
446 }
447
448 auto self = shared_from_this();
449 udp_socket_.async_receive_from(
450 asio::buffer(recv_buffer_),
451 remote_endpoint_,
452 [this, self](std::error_code ec, std::size_t bytes_transferred)
453 {
454 if (!is_receiving_.load())
455 {
456 return;
457 }
458
459 if (ec)
460 {
461 if (ec != asio::error::operation_aborted)
462 {
463 std::lock_guard<std::mutex> lock(callback_mutex_);
464 if (error_cb_)
465 {
466 error_cb_(ec);
467 }
468 }
469 return;
470 }
471
472 if (bytes_transferred > 0)
473 {
474 handle_packet(std::span(recv_buffer_.data(), bytes_transferred));
475 }
476
477 // Continue receiving
478 if (is_receiving_.load())
479 {
480 do_receive();
481 }
482 });
483}
484
485auto quic_socket::handle_packet(std::span<const uint8_t> data) -> void
486{
487 // Parse packet header
488 auto header_result = packet_parser::parse_header(data);
489 if (header_result.is_err())
490 {
491 // Invalid packet - silently ignore
492 return;
493 }
494
495 auto& [header, header_length] = header_result.value();
496
497 // Determine encryption level
498 auto level = determine_encryption_level(header);
499
500 // For server, derive initial secrets on first Initial packet
501 if (role_ == quic_role::server &&
503 {
504 if (std::holds_alternative<long_header>(header))
505 {
506 const auto& lh = std::get<long_header>(header);
507 if (lh.type() == packet_type::initial)
508 {
509 // Use client's DCID as our remote connection ID
510 remote_conn_id_ = lh.src_conn_id;
511
512 // Derive initial secrets from client's DCID
513 auto derive_result = crypto_.derive_initial_secrets(lh.dest_conn_id);
514 if (derive_result.is_err())
515 {
516 return;
517 }
518
519 transition_state(quic_connection_state::handshake);
520 }
521 }
522 }
523
524 // Get read keys for this level
525 auto keys_result = crypto_.get_read_keys(level);
526 if (keys_result.is_err())
527 {
528 // Keys not available yet - queue packet for later processing
529 return;
530 }
531
532 // Unprotect (decrypt) the packet
533 // First we need to get the packet number offset
534 size_t pn_offset = header_length;
535 if (std::holds_alternative<long_header>(header))
536 {
537 // For long headers, packet number is after the header
538 // (already accounted for in header_length for Initial/Handshake)
539 }
540
541 // Determine packet number length from header (after removing header protection)
542 size_t sample_offset = pn_offset + 4; // Sample is 4 bytes after PN start
543 if (sample_offset + hp_sample_size > data.size())
544 {
545 return; // Not enough data for sample
546 }
547
548 std::span<const uint8_t> sample(data.data() + sample_offset, hp_sample_size);
549
550 // Create mutable copy for header unprotection
551 std::vector<uint8_t> packet_copy(data.begin(), data.end());
552
553 auto unprotect_header_result = packet_protection::unprotect_header(
554 keys_result.value(),
555 std::span(packet_copy.data(), header_length + 4), // Include space for max PN
556 pn_offset,
557 sample);
558
559 if (unprotect_header_result.is_err())
560 {
561 return;
562 }
563
564 auto& [first_byte, pn_length] = unprotect_header_result.value();
565
566 // Extract packet number
567 uint64_t truncated_pn = 0;
568 for (size_t i = 0; i < pn_length; ++i)
569 {
570 truncated_pn = (truncated_pn << 8) | packet_copy[pn_offset + i];
571 }
572
573 // Decode full packet number
574 auto level_idx = static_cast<size_t>(level);
575 uint64_t full_pn = packet_number::decode(
576 truncated_pn, pn_length, largest_received_pn_[level_idx]);
577
578 // Update largest received
579 if (full_pn > largest_received_pn_[level_idx])
580 {
581 largest_received_pn_[level_idx] = full_pn;
582 }
583
584 // Decrypt packet payload
585 size_t payload_offset = pn_offset + pn_length;
586 auto unprotect_result = packet_protection::unprotect(
587 keys_result.value(),
588 std::span(packet_copy),
589 payload_offset,
590 full_pn);
591
592 if (unprotect_result.is_err())
593 {
594 return;
595 }
596
597 auto& [unprotected_header, payload] = unprotect_result.value();
598
599 // Parse frames from payload
600 auto frames_result = frame_parser::parse_all(payload);
601 if (frames_result.is_err())
602 {
603 return;
604 }
605
606 // Process each frame
607 for (const auto& f : frames_result.value())
608 {
609 process_frame(f);
610 }
611
612 // Send any pending responses
613 send_pending_packets();
614}
615
616auto quic_socket::process_frame(const frame& f) -> void
617{
618 std::visit([this](auto&& arg) {
619 using T = std::decay_t<decltype(arg)>;
620
621 if constexpr (std::is_same_v<T, crypto_frame>)
622 {
623 process_crypto_frame(arg);
624 }
625 else if constexpr (std::is_same_v<T, stream_frame>)
626 {
627 process_stream_frame(arg);
628 }
629 else if constexpr (std::is_same_v<T, ack_frame>)
630 {
631 process_ack_frame(arg);
632 }
633 else if constexpr (std::is_same_v<T, connection_close_frame>)
634 {
635 process_connection_close_frame(arg);
636 }
637 else if constexpr (std::is_same_v<T, handshake_done_frame>)
638 {
639 process_handshake_done_frame();
640 }
641 else if constexpr (std::is_same_v<T, ping_frame>)
642 {
643 // PING requires ACK - will be sent with next packet
644 }
645 else if constexpr (std::is_same_v<T, padding_frame>)
646 {
647 // PADDING is ignored
648 }
649 // Other frame types can be added as needed
650 }, f);
651}
652
654{
655 auto level = crypto_.current_level();
656
657 // Process the crypto data through TLS
658 auto response_result = crypto_.process_crypto_data(level, f.data);
659 if (response_result.is_err())
660 {
661 return;
662 }
663
664 // Queue any response crypto data
665 if (!response_result.value().empty())
666 {
667 queue_crypto_data(std::move(response_result.value()));
668 }
669
670 // Check if handshake is now complete
671 if (crypto_.is_handshake_complete() && !handshake_complete_.load())
672 {
673 handshake_complete_.store(true);
674
675 // For client, transition to connected
676 // For server, we send HANDSHAKE_DONE
677 if (role_ == quic_role::client)
678 {
679 transition_state(quic_connection_state::connected);
680
681 std::lock_guard<std::mutex> lock(callback_mutex_);
682 if (connected_cb_)
683 {
684 connected_cb_();
685 }
686 }
687 else
688 {
689 // Server sends HANDSHAKE_DONE
690 std::vector<frame> frames;
691 frames.push_back(handshake_done_frame{});
692
693 (void)send_packet(encryption_level::application, std::move(frames));
694
695 transition_state(quic_connection_state::connected);
696
697 std::lock_guard<std::mutex> lock(callback_mutex_);
698 if (connected_cb_)
699 {
700 connected_cb_();
701 }
702 }
703 }
704}
705
707{
708 std::lock_guard<std::mutex> lock(callback_mutex_);
709 if (stream_data_cb_)
710 {
711 stream_data_cb_(f.stream_id, f.data, f.fin);
712 }
713}
714
716{
717 // Process ACK - remove acknowledged packets from retransmission queue
718 // For now, just track the largest acknowledged
719 (void)f; // Placeholder for full implementation
720}
721
723{
724 transition_state(quic_connection_state::draining);
725
726 std::lock_guard<std::mutex> lock(callback_mutex_);
727 if (close_cb_)
728 {
729 close_cb_(f.error_code, f.reason_phrase);
730 }
731
732 // Enter draining period
733 idle_timer_.expires_after(std::chrono::milliseconds(300));
734 idle_timer_.async_wait(
735 [self = shared_from_this()](const std::error_code& ec)
736 {
737 if (!ec)
738 {
739 self->transition_state(quic_connection_state::closed);
740 self->stop_receive();
741 }
742 });
743}
744
746{
747 // Client receives HANDSHAKE_DONE
748 if (role_ == quic_role::client && !handshake_complete_.load())
749 {
750 handshake_complete_.store(true);
751 transition_state(quic_connection_state::connected);
752
753 std::lock_guard<std::mutex> lock(callback_mutex_);
754 if (connected_cb_)
755 {
756 connected_cb_();
757 }
758 }
759}
760
762{
763 auto current_state = state_.load();
764 if (current_state == quic_connection_state::closed ||
765 current_state == quic_connection_state::idle)
766 {
767 return;
768 }
769
770 // Determine which encryption level to use
771 encryption_level level = crypto_.current_level();
772
773 std::vector<frame> frames;
774
775 // Add pending CRYPTO data
776 {
777 std::lock_guard<std::mutex> lock(state_mutex_);
778 auto level_idx = static_cast<size_t>(level);
779 while (!pending_crypto_data_[level_idx].empty())
780 {
781 auto& data = pending_crypto_data_[level_idx].front();
782 crypto_frame cf;
783 cf.offset = 0; // Simplified - full impl needs offset tracking
784 cf.data = std::move(data);
785 frames.push_back(std::move(cf));
786 pending_crypto_data_[level_idx].pop_front();
787 }
788 }
789
790 // Add pending STREAM data (only if connected)
791 if (is_connected())
792 {
793 std::lock_guard<std::mutex> lock(state_mutex_);
794 for (auto& [stream_id, queue] : pending_stream_data_)
795 {
796 while (!queue.empty())
797 {
798 auto& [data, fin] = queue.front();
799 stream_frame sf;
800 sf.stream_id = stream_id;
801 sf.offset = 0; // Simplified - full impl needs offset tracking
802 sf.data = std::move(data);
803 sf.fin = fin;
804 frames.push_back(std::move(sf));
805 queue.pop_front();
806 }
807 }
808 }
809
810 if (!frames.empty())
811 {
812 (void)send_packet(level, std::move(frames));
813 }
814}
815
817 std::vector<frame>&& frames) -> VoidResult
818{
819 // Get write keys
820 auto keys_result = crypto_.get_write_keys(level);
821 if (keys_result.is_err())
822 {
823 return error_void(
825 "Write keys not available",
826 "quic_socket");
827 }
828
829 // Build frames payload
830 std::vector<uint8_t> payload;
831 for (const auto& f : frames)
832 {
833 auto frame_bytes = frame_builder::build(f);
834 payload.insert(payload.end(), frame_bytes.begin(), frame_bytes.end());
835 }
836
837 // Get next packet number
838 auto level_idx = static_cast<size_t>(level);
839 uint64_t pn = next_packet_number_[level_idx]++;
840
841 // Build header
842 std::vector<uint8_t> header;
843 if (level == encryption_level::initial)
844 {
846 remote_conn_id_, local_conn_id_, {}, pn);
847 }
848 else if (level == encryption_level::handshake)
849 {
851 remote_conn_id_, local_conn_id_, pn);
852 }
853 else
854 {
856 remote_conn_id_, pn, crypto_.key_phase());
857 }
858
859 // Protect (encrypt) the packet
860 auto protect_result = packet_protection::protect(
861 keys_result.value(), header, payload, pn);
862
863 if (protect_result.is_err())
864 {
865 return error_void(
867 "Failed to protect packet",
868 "quic_socket",
869 protect_result.error().message);
870 }
871
872 // Send the packet
873 auto& protected_packet = protect_result.value();
874
875 auto self = shared_from_this();
876 auto buffer = std::make_shared<std::vector<uint8_t>>(std::move(protected_packet));
877
878 udp_socket_.async_send_to(
879 asio::buffer(*buffer),
880 remote_endpoint_,
881 [self, buffer](std::error_code ec, std::size_t /*bytes_sent*/)
882 {
883 if (ec && ec != asio::error::operation_aborted)
884 {
885 std::lock_guard<std::mutex> lock(self->callback_mutex_);
886 if (self->error_cb_)
887 {
888 self->error_cb_(ec);
889 }
890 }
891 });
892
893 return ok();
894}
895
896auto quic_socket::queue_crypto_data(std::vector<uint8_t>&& data) -> void
897{
898 auto level = crypto_.current_level();
899 auto level_idx = static_cast<size_t>(level);
900
901 std::lock_guard<std::mutex> lock(state_mutex_);
902 pending_crypto_data_[level_idx].push_back(std::move(data));
903}
904
907{
908 if (std::holds_alternative<long_header>(header))
909 {
910 const auto& lh = std::get<long_header>(header);
911 switch (lh.type())
912 {
913 case packet_type::initial:
914 return encryption_level::initial;
915 case packet_type::zero_rtt:
916 return encryption_level::zero_rtt;
917 case packet_type::handshake:
918 return encryption_level::handshake;
919 default:
920 return encryption_level::initial;
921 }
922 }
923 else
924 {
925 // Short header = 1-RTT = application level
926 return encryption_level::application;
927 }
928}
929
931{
932 std::random_device rd;
933 std::mt19937 gen(rd());
934 std::uniform_int_distribution<unsigned int> dis(0, 255);
935
936 std::array<uint8_t, 8> id_bytes;
937 for (auto& byte : id_bytes)
938 {
939 byte = static_cast<uint8_t>(dis(gen));
940 }
941
942 return connection_id(std::span<const uint8_t>(id_bytes));
943}
944
946{
947 // Retransmission logic would go here
948 // For now, just resend pending packets
949 send_pending_packets();
950}
951
953{
954 state_.store(new_state);
955}
956
957} // namespace kcenon::network::internal
A QUIC socket that wraps UDP and integrates QUIC packet protection.
Definition quic_socket.h:73
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:81
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.
Definition quic_socket.h:95
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.
std::function< void()> connected_callback
Callback when connection is established.
Definition quic_socket.h:89
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.
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:891
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:467
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:578
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:423
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:168
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:687
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:508
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:425
quic_role
Role of the QUIC endpoint (client or server)
Definition quic_socket.h:31
quic_connection_state
QUIC connection state machine states.
Definition quic_socket.h:41
@ 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.