| /** @file |
| * |
| * A brief file description |
| * |
| * @section license License |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include <cstring> |
| #include <unordered_map> |
| #include "tscore/ink_endian.h" |
| #include "tscore/ink_hrtime.h" |
| #include "tscore/Ptr.h" |
| #include "I_EventSystem.h" |
| |
| #include "I_NetVConnection.h" |
| |
| #include <memory> |
| #include <random> |
| #include <cstdint> |
| #include <string> |
| #include "tscore/ink_memory.h" |
| #include "tscore/ink_inet.h" |
| #include <openssl/evp.h> |
| |
| using QUICPacketNumber = uint64_t; |
| using QUICVersion = uint32_t; |
| using QUICStreamId = uint64_t; |
| using QUICOffset = uint64_t; |
| using QUICFrameId = uint64_t; |
| |
| // TODO: Update version number |
| // Note: Prefix for drafts (0xff000000) + draft number |
| // Note: Fix "Supported Version" field in test case of QUICPacketFactory_Create_VersionNegotiationPacket |
| // Note: Fix QUIC_ALPN_PROTO_LIST in QUICConfig.cc |
| // Note: Change ExtensionType (QUICTransportParametersHandler::TRANSPORT_PARAMETER_ID) if it's changed |
| constexpr QUICVersion QUIC_SUPPORTED_VERSIONS[] = { |
| 0xff00001d, |
| 0xff00001b, |
| }; |
| constexpr QUICVersion QUIC_EXERCISE_VERSION1 = 0x1a2a3a4a; |
| constexpr QUICVersion QUIC_EXERCISE_VERSION2 = 0x5a6a7a8a; |
| |
| enum class QUICEncryptionLevel { |
| NONE = -1, |
| INITIAL = 0, |
| ZERO_RTT = 1, |
| HANDSHAKE = 2, |
| ONE_RTT = 3, |
| }; |
| |
| // For range-based for loop. This starts from INITIAL to ONE_RTT. It doesn't include NONE. |
| // Defining begin, end, operator*, operator++ doesn't work for duplicate symbol issue with libmgmt_p.a :( |
| // TODO: support ZERO_RTT |
| constexpr QUICEncryptionLevel QUIC_ENCRYPTION_LEVELS[] = { |
| QUICEncryptionLevel::INITIAL, |
| QUICEncryptionLevel::ZERO_RTT, |
| QUICEncryptionLevel::HANDSHAKE, |
| QUICEncryptionLevel::ONE_RTT, |
| }; |
| |
| // kPacketNumberSpace on Recovery A.2.Constants of Interest |
| enum class QUICPacketNumberSpace : int { INITIAL, HANDSHAKE, APPLICATION_DATA, N_SPACES }; |
| // For conveniece (this removes neccesity of static_cast) |
| constexpr int QUIC_N_PACKET_SPACES = static_cast<int>(QUICPacketNumberSpace::N_SPACES); |
| |
| // Divide to QUICPacketType and QUICPacketLongHeaderType ? |
| enum class QUICPacketType : uint8_t { |
| INITIAL = 0x00, // draft-17 version-specific type |
| ZERO_RTT_PROTECTED = 0x01, // draft-17 version-specific type |
| HANDSHAKE = 0x02, // draft-17 version-specific type |
| RETRY = 0x03, // draft-17 version-specific type |
| VERSION_NEGOTIATION = 0xF0, // Not on the spec. but just for convenience |
| PROTECTED, // Not on the spec. but just for convenience |
| STATELESS_RESET, // Not on the spec. but just for convenience |
| UNINITIALIZED = 0xFF, // Not on the spec. but just for convenience |
| }; |
| |
| // XXX If you add or remove QUICFrameType, you might also need to change QUICFrame::type(const uint8_t *) |
| enum class QUICFrameType : uint8_t { |
| PADDING = 0x00, |
| PING, |
| ACK, |
| ACK_WITH_ECN, |
| RESET_STREAM = 0x04, |
| STOP_SENDING, |
| CRYPTO, |
| NEW_TOKEN, |
| STREAM, // 0x08 - 0x0f |
| MAX_DATA = 0x10, |
| MAX_STREAM_DATA, |
| MAX_STREAMS, // 0x12 - 0x13 |
| DATA_BLOCKED = 0x14, |
| STREAM_DATA_BLOCKED, |
| STREAMS_BLOCKED, // 0x16 - 0x17 |
| NEW_CONNECTION_ID = 0x18, |
| RETIRE_CONNECTION_ID, |
| PATH_CHALLENGE, |
| PATH_RESPONSE, |
| CONNECTION_CLOSE, // 0x1c - 0x1d |
| HANDSHAKE_DONE = 0x1e, |
| UNKNOWN = 0x1f, |
| }; |
| |
| enum class QUICVersionNegotiationStatus { |
| NOT_NEGOTIATED, // Haven't negotiated yet |
| NEGOTIATED, // Negotiated |
| VALIDATED, // Validated with a one in transport parameters |
| FAILED, // Negotiation failed |
| }; |
| |
| enum class QUICKeyPhase : int { |
| PHASE_0 = 0, |
| PHASE_1, |
| INITIAL, |
| ZERO_RTT, |
| HANDSHAKE, |
| }; |
| |
| enum class QUICPacketCreationResult { |
| SUCCESS, |
| FAILED, |
| NO_PACKET, |
| NOT_READY, |
| IGNORED, |
| UNSUPPORTED, |
| }; |
| |
| enum class QUICErrorClass { |
| UNDEFINED, |
| TRANSPORT, |
| APPLICATION, |
| }; |
| |
| enum class QUICTransErrorCode : uint64_t { |
| NO_ERROR = 0x00, |
| INTERNAL_ERROR, |
| CONNECTION_REFUSED, |
| FLOW_CONTROL_ERROR, |
| STREAM_LIMIT_ERROR, |
| STREAM_STATE_ERROR, |
| FINAL_SIZE_ERROR, |
| FRAME_ENCODING_ERROR, |
| TRANSPORT_PARAMETER_ERROR, |
| CONNECTION_ID_LIMIT_ERROR, |
| PROTOCOL_VIOLATION, |
| INVALID_TOKEN, |
| APPLICATION_ERROR, |
| CRYPTO_BUFFER_EXCEEDED, |
| CRYPTO_ERROR = 0x0100, // 0x100 - 0x1FF |
| }; |
| |
| // Application Protocol Error Codes defined in application |
| using QUICAppErrorCode = uint64_t; |
| constexpr uint16_t QUIC_APP_ERROR_CODE_STOPPING = 0; |
| |
| class QUICError |
| { |
| public: |
| virtual ~QUICError() {} |
| |
| QUICErrorClass cls = QUICErrorClass::UNDEFINED; |
| uint16_t code = 0; |
| const char *msg = nullptr; |
| |
| protected: |
| QUICError(){}; |
| QUICError(QUICErrorClass error_class, uint16_t error_code, const char *error_msg = nullptr) |
| : cls(error_class), code(error_code), msg(error_msg) |
| { |
| } |
| }; |
| |
| class QUICConnectionError : public QUICError |
| { |
| public: |
| QUICConnectionError() : QUICError() {} |
| QUICConnectionError(QUICTransErrorCode error_code, const char *error_msg = nullptr, |
| QUICFrameType frame_type = QUICFrameType::UNKNOWN) |
| : QUICError(QUICErrorClass::TRANSPORT, static_cast<uint16_t>(error_code), error_msg), _frame_type(frame_type){}; |
| QUICConnectionError(QUICErrorClass error_class, uint16_t error_code, const char *error_msg = nullptr, |
| QUICFrameType frame_type = QUICFrameType::UNKNOWN) |
| : QUICError(error_class, error_code, error_msg), _frame_type(frame_type){}; |
| |
| QUICFrameType frame_type() const; |
| |
| private: |
| QUICFrameType _frame_type = QUICFrameType::UNKNOWN; |
| }; |
| |
| class QUICStream; |
| |
| class QUICStreamError : public QUICError |
| { |
| public: |
| QUICStreamError() : QUICError() {} |
| QUICStreamError(const QUICStream *s, const QUICTransErrorCode error_code, const char *error_msg = nullptr) |
| : QUICError(QUICErrorClass::TRANSPORT, static_cast<uint16_t>(error_code), error_msg), stream(s){}; |
| QUICStreamError(const QUICStream *s, const QUICAppErrorCode error_code, const char *error_msg = nullptr) |
| : QUICError(QUICErrorClass::APPLICATION, static_cast<uint16_t>(error_code), error_msg), stream(s){}; |
| |
| const QUICStream *stream; |
| }; |
| |
| using QUICErrorUPtr = std::unique_ptr<QUICError>; |
| using QUICConnectionErrorUPtr = std::unique_ptr<QUICConnectionError>; |
| using QUICStreamErrorUPtr = std::unique_ptr<QUICStreamError>; |
| |
| class QUICConnectionId |
| { |
| public: |
| static uint8_t SCID_LEN; |
| |
| static constexpr int MIN_LENGTH_FOR_INITIAL = 8; |
| static constexpr int MAX_LENGTH = 20; |
| static constexpr size_t MAX_HEX_STR_LENGTH = MAX_LENGTH * 2 + 1; |
| static QUICConnectionId ZERO(); |
| QUICConnectionId(); |
| QUICConnectionId(const uint8_t *buf, uint8_t len); |
| |
| explicit operator bool() const { return true; } |
| /** |
| * Note that this returns a kind of hash code so we can use a ConnectionId as a key for a hashtable. |
| */ |
| operator uint64_t() const { return this->_hashcode(); } |
| operator const uint8_t *() const { return this->_id; } |
| bool |
| operator==(const QUICConnectionId &x) const |
| { |
| if (this->_len != x._len) { |
| return false; |
| } |
| return memcmp(this->_id, x._id, this->_len) == 0; |
| } |
| |
| bool |
| operator!=(const QUICConnectionId &x) const |
| { |
| if (this->_len != x._len) { |
| return true; |
| } |
| return memcmp(this->_id, x._id, this->_len) != 0; |
| } |
| |
| /* |
| * This is just for debugging. |
| */ |
| uint32_t h32() const; |
| std::string hex() const; |
| |
| uint8_t length() const; |
| bool is_zero() const; |
| void randomize(); |
| |
| private: |
| uint64_t _hashcode() const; |
| uint8_t _id[MAX_LENGTH]; |
| uint8_t _len = 0; |
| }; |
| |
| class QUICStatelessResetToken |
| { |
| public: |
| constexpr static int8_t LEN = 16; |
| |
| QUICStatelessResetToken() {} |
| QUICStatelessResetToken(const QUICConnectionId &conn_id, uint32_t instance_id); |
| QUICStatelessResetToken(const uint8_t *buf) { memcpy(this->_token, buf, QUICStatelessResetToken::LEN); } |
| |
| /** |
| * Note that this returns a kind of hash code so we can use a StatelessResetToken as a key for a hashtable. |
| */ |
| operator uint64_t() const { return this->_hashcode(); } |
| |
| bool |
| operator==(const QUICStatelessResetToken &x) const |
| { |
| return memcmp(this->_token, x._token, QUICStatelessResetToken::LEN) == 0; |
| } |
| |
| bool |
| operator!=(const QUICStatelessResetToken &x) const |
| { |
| return memcmp(this->_token, x._token, QUICStatelessResetToken::LEN) != 0; |
| } |
| |
| const uint8_t * |
| buf() const |
| { |
| return _token; |
| } |
| |
| std::string hex() const; |
| |
| private: |
| uint8_t _token[LEN] = {0}; |
| |
| void _generate(uint64_t data); |
| uint64_t _hashcode() const; |
| }; |
| |
| class QUICAddressValidationToken |
| { |
| public: |
| enum class Type : uint8_t { |
| RESUMPTION, |
| RETRY, |
| }; |
| |
| // FIXME Check token length |
| QUICAddressValidationToken(const uint8_t *buf, size_t len) : _token_len(len) { memcpy(this->_token, buf, len); } |
| virtual ~QUICAddressValidationToken(){}; |
| |
| static Type |
| type(const uint8_t *buf) |
| { |
| ink_assert(static_cast<Type>(buf[0]) == Type::RESUMPTION || static_cast<Type>(buf[0]) == Type::RETRY); |
| return static_cast<Type>(buf[0]) == Type::RESUMPTION ? Type::RESUMPTION : Type::RETRY; |
| } |
| |
| virtual const uint8_t * |
| buf() const |
| { |
| return this->_token; |
| } |
| |
| virtual uint8_t |
| length() const |
| { |
| return this->_token_len; |
| } |
| |
| protected: |
| QUICAddressValidationToken() {} |
| |
| // The size should be smaller than maximum size of Retry packet |
| uint8_t _token[1200] = {0}; |
| unsigned int _token_len; |
| }; |
| |
| class QUICResumptionToken : public QUICAddressValidationToken |
| { |
| public: |
| QUICResumptionToken(const uint8_t *buf, uint8_t len) : QUICAddressValidationToken(buf, len) {} |
| QUICResumptionToken(const IpEndpoint &src, QUICConnectionId cid, ink_hrtime expire_time); |
| |
| bool |
| operator==(const QUICResumptionToken &x) const |
| { |
| if (this->_token_len != x._token_len) { |
| return false; |
| } |
| return memcmp(this->_token, x._token, this->_token_len) == 0; |
| } |
| |
| bool is_valid(const IpEndpoint &src) const; |
| |
| const QUICConnectionId cid() const; |
| ink_hrtime expire_time() const; |
| }; |
| |
| class QUICRetryToken : public QUICAddressValidationToken |
| { |
| public: |
| QUICRetryToken(const uint8_t *buf, size_t len) : QUICAddressValidationToken(buf, len) {} |
| QUICRetryToken(const IpEndpoint &src, QUICConnectionId original_dcid, QUICConnectionId scid); |
| |
| bool |
| operator==(const QUICRetryToken &x) const |
| { |
| if (this->_token_len != x._token_len) { |
| return false; |
| } |
| return memcmp(this->_token, x._token, this->_token_len) == 0; |
| } |
| |
| bool is_valid(const IpEndpoint &src) const; |
| |
| const QUICConnectionId original_dcid() const; |
| const QUICConnectionId scid() const; |
| }; |
| |
| class QUICPreferredAddress |
| { |
| public: |
| constexpr static int16_t MIN_LEN = 41; |
| constexpr static int16_t MAX_LEN = 61; |
| |
| QUICPreferredAddress(IpEndpoint endpoint_ipv4, IpEndpoint endpoint_ipv6, const QUICConnectionId &cid, |
| QUICStatelessResetToken token) |
| : _endpoint_ipv4(endpoint_ipv4), _endpoint_ipv6(endpoint_ipv6), _cid(cid), _token(token), _valid(true) |
| { |
| } |
| QUICPreferredAddress(const uint8_t *buf, uint16_t len); |
| |
| bool is_available() const; |
| bool has_ipv4() const; |
| bool has_ipv6() const; |
| const IpEndpoint endpoint_ipv4() const; |
| const IpEndpoint endpoint_ipv6() const; |
| const QUICConnectionId cid() const; |
| const QUICStatelessResetToken token() const; |
| |
| void store(uint8_t *buf, uint16_t &len) const; |
| |
| private: |
| IpEndpoint _endpoint_ipv4 = {}; |
| IpEndpoint _endpoint_ipv6 = {}; |
| QUICConnectionId _cid; |
| QUICStatelessResetToken _token; |
| bool _valid = false; |
| }; |
| |
| enum class QUICStreamType : uint8_t { |
| CLIENT_BIDI = 0x00, |
| SERVER_BIDI, |
| CLIENT_UNI, |
| SERVER_UNI, |
| }; |
| |
| enum class QUICStreamDirection : uint8_t { |
| UNKNOWN = 0, |
| SEND, |
| RECEIVE, |
| BIDIRECTIONAL, |
| }; |
| |
| class QUICFiveTuple |
| { |
| public: |
| QUICFiveTuple(){}; |
| QUICFiveTuple(IpEndpoint src, IpEndpoint dst, int protocol); |
| void update(IpEndpoint src, IpEndpoint dst, int protocol); |
| IpEndpoint source() const; |
| IpEndpoint destination() const; |
| int protocol() const; |
| |
| private: |
| IpEndpoint _source; |
| IpEndpoint _destination; |
| int _protocol; |
| uint64_t _hash_code = 0; |
| }; |
| |
| class QUICPath |
| { |
| public: |
| QUICPath(IpEndpoint local_ep, IpEndpoint remote_ep); |
| const IpEndpoint &local_ep() const; |
| const IpEndpoint &remote_ep() const; |
| |
| inline bool |
| operator==(const QUICPath &x) const |
| { |
| if ((this->_local_ep.port() != 0 && x._local_ep.port() != 0) && this->_local_ep.port() != x._local_ep.port()) { |
| return false; |
| } |
| |
| if ((this->_remote_ep.port() != 0 && x._remote_ep.port() != 0) && this->_remote_ep.port() != x._remote_ep.port()) { |
| return false; |
| } |
| |
| if ((!IpAddr(this->_local_ep).isAnyAddr() && !IpAddr(x._local_ep).isAnyAddr()) && this->_local_ep != x._local_ep) { |
| return false; |
| } |
| |
| if ((!IpAddr(this->_remote_ep).isAnyAddr() || !IpAddr(x._remote_ep).isAnyAddr()) && this->_remote_ep != x._remote_ep) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| private: |
| IpEndpoint _local_ep; |
| IpEndpoint _remote_ep; |
| }; |
| |
| class QUICPathHasher |
| { |
| public: |
| std::size_t |
| operator()(const QUICPath &k) const |
| { |
| return k.remote_ep().port(); |
| } |
| }; |
| |
| class QUICPathValidationData |
| { |
| public: |
| QUICPathValidationData(const uint8_t *data) { memcpy(this->_data, data, sizeof(this->_data)); } |
| |
| inline operator const uint8_t *() const { return this->_data; } |
| |
| private: |
| uint8_t _data[8]; |
| }; |
| |
| class QUICTPConfig |
| { |
| public: |
| virtual ~QUICTPConfig() = default; // required |
| virtual uint32_t no_activity_timeout() const = 0; |
| virtual const IpEndpoint *preferred_address_ipv4() const = 0; |
| virtual const IpEndpoint *preferred_address_ipv6() const = 0; |
| virtual uint32_t initial_max_data() const = 0; |
| virtual uint32_t initial_max_stream_data_bidi_local() const = 0; |
| virtual uint32_t initial_max_stream_data_bidi_remote() const = 0; |
| virtual uint32_t initial_max_stream_data_uni() const = 0; |
| virtual uint64_t initial_max_streams_bidi() const = 0; |
| virtual uint64_t initial_max_streams_uni() const = 0; |
| virtual uint8_t ack_delay_exponent() const = 0; |
| virtual uint8_t max_ack_delay() const = 0; |
| virtual uint8_t active_cid_limit() const = 0; |
| virtual bool disable_active_migration() const = 0; |
| virtual const std::unordered_map<uint16_t, std::pair<const uint8_t *, uint16_t>> &additional_tp() const = 0; |
| }; |
| |
| class QUICLDConfig |
| { |
| public: |
| virtual ~QUICLDConfig() {} |
| virtual uint32_t packet_threshold() const = 0; |
| virtual float time_threshold() const = 0; |
| virtual ink_hrtime granularity() const = 0; |
| virtual ink_hrtime initial_rtt() const = 0; |
| }; |
| |
| class QUICCCConfig |
| { |
| public: |
| virtual ~QUICCCConfig() {} |
| virtual uint32_t initial_window() const = 0; |
| virtual uint32_t minimum_window() const = 0; |
| virtual float loss_reduction_factor() const = 0; |
| virtual uint32_t persistent_congestion_threshold() const = 0; |
| }; |
| |
| class QUICFrameGenerator; |
| |
| struct QUICSentPacketInfo { |
| class FrameInfo |
| { |
| public: |
| FrameInfo(QUICFrameId id, QUICFrameGenerator *generator) : _id(id), _generator(generator) {} |
| |
| QUICFrameId id() const; |
| QUICFrameGenerator *generated_by() const; |
| |
| private: |
| QUICFrameId _id = 0; |
| QUICFrameGenerator *_generator; |
| }; |
| |
| // Recovery A.1.1. Sent Packet Fields |
| QUICPacketNumber packet_number; |
| bool ack_eliciting; |
| bool in_flight; |
| size_t sent_bytes; |
| ink_hrtime time_sent; |
| |
| // Additional fields |
| QUICPacketType type; |
| std::vector<FrameInfo> frames; |
| QUICPacketNumberSpace pn_space; |
| // End of additional fields |
| }; |
| |
| using QUICSentPacketInfoUPtr = std::unique_ptr<QUICSentPacketInfo>; |
| |
| class QUICRTTProvider |
| { |
| public: |
| virtual ~QUICRTTProvider() {} // required - class has virtual methods. |
| virtual ink_hrtime smoothed_rtt() const = 0; |
| virtual ink_hrtime rttvar() const = 0; |
| virtual ink_hrtime latest_rtt() const = 0; |
| |
| virtual ink_hrtime congestion_period(uint32_t threshold) const = 0; |
| }; |
| |
| // TODO: move version independent functions to QUICInvariants |
| class QUICTypeUtil |
| { |
| public: |
| static bool is_supported_version(QUICVersion version); |
| static QUICStreamType detect_stream_type(QUICStreamId id); |
| static QUICStreamDirection detect_stream_direction(QUICStreamId id, NetVConnectionContext_t context); |
| static QUICEncryptionLevel encryption_level(QUICPacketType type); |
| static QUICPacketType packet_type(QUICEncryptionLevel level); |
| static QUICKeyPhase key_phase(QUICPacketType type); |
| static QUICPacketNumberSpace pn_space(QUICEncryptionLevel level); |
| |
| static QUICConnectionId read_QUICConnectionId(const uint8_t *buf, uint8_t n); |
| static int read_QUICPacketNumberLen(const uint8_t *buf); |
| static QUICPacketNumber read_QUICPacketNumber(const uint8_t *buf, int len); |
| static QUICVersion read_QUICVersion(const uint8_t *buf); |
| static QUICStreamId read_QUICStreamId(const uint8_t *buf, size_t buf_len); |
| static QUICOffset read_QUICOffset(const uint8_t *buf, size_t buf_len); |
| static uint16_t read_QUICTransErrorCode(const uint8_t *buf); |
| static QUICAppErrorCode read_QUICAppErrorCode(const uint8_t *buf); |
| static uint64_t read_QUICMaxData(const uint8_t *buf, size_t buf_len); |
| |
| static void write_QUICConnectionId(QUICConnectionId connection_id, uint8_t *buf, size_t *len); |
| static void write_QUICPacketNumberLen(int len, uint8_t *buf); |
| static void write_QUICPacketNumber(QUICPacketNumber packet_number, uint8_t n, uint8_t *buf, size_t *len); |
| static void write_QUICVersion(QUICVersion version, uint8_t *buf, size_t *len); |
| static void write_QUICStreamId(QUICStreamId stream_id, uint8_t *buf, size_t *len); |
| static void write_QUICOffset(QUICOffset offset, uint8_t *buf, size_t *len); |
| static void write_QUICTransErrorCode(uint64_t error_code, uint8_t *buf, size_t *len); |
| static void write_QUICAppErrorCode(QUICAppErrorCode error_code, uint8_t *buf, size_t *len); |
| static void write_QUICMaxData(uint64_t max_data, uint8_t *buf, size_t *len); |
| |
| private: |
| }; |
| |
| class QUICInvariants |
| { |
| public: |
| static bool is_long_header(const uint8_t *buf); |
| static bool is_version_negotiation(QUICVersion v); |
| static bool version(QUICVersion &dst, const uint8_t *buf, uint64_t buf_len); |
| static bool dcil(uint8_t &dst, const uint8_t *buf, uint64_t buf_len); |
| static bool scil(uint8_t &dst, const uint8_t *buf, uint64_t buf_len); |
| static bool dcid(QUICConnectionId &dst, const uint8_t *buf, uint64_t buf_len); |
| static bool scid(QUICConnectionId &dst, const uint8_t *buf, uint64_t buf_len); |
| |
| static const size_t LH_VERSION_OFFSET = 1; |
| static const size_t LH_CIL_OFFSET = 5; |
| static const size_t LH_DCID_OFFSET = 6; |
| static const size_t SH_DCID_OFFSET = 1; |
| static const size_t LH_MIN_LEN = 6; |
| static const size_t SH_MIN_LEN = 1; |
| }; |
| |
| int to_hex_str(char *dst, size_t dst_len, const uint8_t *src, size_t src_len); |
| |
| namespace QUICBase |
| { |
| std::string to_hex(const uint8_t *buf, size_t len); |
| |
| } // namespace QUICBase |