| // Copyright 2019 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef QUICHE_QUIC_MASQUE_MASQUE_UTILS_H_ | |
| #define QUICHE_QUIC_MASQUE_MASQUE_UTILS_H_ | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <optional> | |
| #include <ostream> | |
| #include <string> | |
| #include <vector> | |
| #include "absl/strings/string_view.h" | |
| #include "quiche/quic/core/http/quic_spdy_stream.h" | |
| #include "quiche/quic/core/quic_types.h" | |
| #include "quiche/quic/core/quic_versions.h" | |
| #include "quiche/quic/platform/api/quic_ip_address.h" | |
| #include "quiche/common/platform/api/quiche_export.h" | |
| namespace quic { | |
| // List of QUIC versions that support MASQUE. Currently restricted to IETF QUIC. | |
| QUICHE_EXPORT ParsedQuicVersionVector MasqueSupportedVersions(); | |
| inline constexpr QuicByteCount kMasqueIpPacketBufferSize = 1501; | |
| // Enough for a VLAN tag, but not Stacked VLANs. | |
| inline constexpr QuicByteCount kMasqueEthernetFrameBufferSize = 1523; | |
| // Mode that MASQUE is operating in. | |
| enum class MasqueMode : uint8_t { | |
| kInvalid = 0, // Should never be used. | |
| kOpen = 2, // Open mode uses the MASQUE HTTP CONNECT-UDP method as documented | |
| // in <https://www.rfc-editor.org/rfc/rfc9298.html>. This mode allows | |
| // unauthenticated clients (a more restricted mode will be added to this enum | |
| // at a later date). | |
| kConnectIp = | |
| 1, // ConnectIp mode uses MASQUE HTTP CONNECT-IP as documented in | |
| // <https://datatracker.ietf.org/doc/html/draft-ietf-masque-connect-ip>. This | |
| // mode also allows unauthenticated clients. | |
| kConnectEthernet = | |
| 3, // ConnectEthernet mode uses MASQUE HTTP CONNECT-ETHERNET. | |
| // <https://datatracker.ietf.org/doc/draft-asedeno-masque-connect-ethernet/> | |
| // This mode also allows unauthenticated clients. | |
| kConnectUdpBind = 4, | |
| // Bind a UDP socket on the proxy and forward all packets to the client. | |
| // This mode uses MASQUE HTTP CONNECT-UDP-BIND. | |
| // We define a custom handler for this. Our default handler simply echoes | |
| // back the UDP Payload. | |
| // <https://datatracker.ietf.org/doc/draft-ietf-masque-connect-udp-listen/> | |
| }; | |
| QUICHE_EXPORT std::string MasqueModeToString(MasqueMode masque_mode); | |
| QUICHE_EXPORT std::ostream& operator<<(std::ostream& os, | |
| const MasqueMode& masque_mode); | |
| // Create a TUN interface, with the specified `client_address`. Requires root. | |
| int CreateTunInterface(const QuicIpAddress& client_address, bool server = true); | |
| // Create a TAP interface. Requires root. | |
| int CreateTapInterface(); | |
| inline constexpr size_t kConcealedAuthSignatureInputSize = 32; | |
| inline constexpr size_t kConcealedAuthVerificationSize = 16; | |
| inline constexpr size_t kConcealedAuthExporterSize = | |
| kConcealedAuthSignatureInputSize + kConcealedAuthVerificationSize; | |
| inline constexpr uint16_t kEd25519SignatureScheme = 0x0807; | |
| inline constexpr absl::string_view kConcealedAuthLabel = | |
| "EXPORTER-HTTP-Concealed-Authentication"; | |
| // Returns the signature auth TLS key exporter context. | |
| QUICHE_EXPORT std::string ComputeConcealedAuthContext( | |
| uint16_t signature_scheme, absl::string_view key_id, | |
| absl::string_view public_key, absl::string_view scheme, | |
| absl::string_view host, uint16_t port, absl::string_view realm); | |
| // Returns the data covered by signature auth signatures, computed by | |
| // concatenating a fixed prefix from the specification and the signature input. | |
| QUICHE_EXPORT std::string ConcealedAuthDataCoveredBySignature( | |
| absl::string_view signature_input); | |
| // Capsule types for draft-ietf-masque-connect-ip-dns-06. | |
| // Provisional values registered in Section 6 (Table 1). | |
| inline constexpr uint64_t kDnsAssignCapsuleType = 0x1ACE79EC; | |
| inline constexpr uint64_t kPref64CapsuleType = 0x274C0FBC; | |
| // Represents a Domain structure (Section 3.1), which carries a domain name | |
| // in format of an IDNA A-label. | |
| struct QUICHE_EXPORT DnsDomain { | |
| // Fully Qualified Domain Name in DNS presentation format (ASCII/A-label). | |
| std::string domain_name; | |
| bool operator==(const DnsDomain& other) const { | |
| return domain_name == other.domain_name; | |
| } | |
| }; | |
| // Represents a Nameserver structure (Section 3.2), detailing how to reach a | |
| // particular DNS resolver using unencrypted or encrypted transports. | |
| struct QUICHE_EXPORT DnsNameserver { | |
| // The priority of this nameserver compared to others | |
| // (Section 2.4.1 of [SVCB]). | |
| // Note: MUST NOT be set to 0 per Section 3.2 of the CONNECT-IP DNS draft. | |
| uint16_t service_priority = 0; | |
| // Sequence of IPv4 addresses that can be used to reach this nameserver. | |
| std::vector<QuicIpAddress> ipv4_addresses; | |
| // Sequence of IPv6 addresses that can be used to reach this nameserver. | |
| std::vector<QuicIpAddress> ipv6_addresses; | |
| // Domain representing the domain name of the nameserver. This may be empty | |
| // if the nameserver only supports unencrypted DNS over port 53. | |
| DnsDomain authentication_domain_name; | |
| // Set of service parameters applying to this nameserver encoded using the | |
| // wire format specified in Section 2.2 of [SVCB] (RFC 9460). | |
| // Use EncodeSvcParams() / DecodeSvcParams() to convert between presentation | |
| // format and wire format. | |
| std::string service_parameters; | |
| bool operator==(const DnsNameserver& other) const { | |
| return service_priority == other.service_priority && | |
| ipv4_addresses == other.ipv4_addresses && | |
| ipv6_addresses == other.ipv6_addresses && | |
| authentication_domain_name == other.authentication_domain_name && | |
| service_parameters == other.service_parameters; | |
| } | |
| std::string ToString() const; | |
| }; | |
| // Encodes service parameters from DNS presentation format (e.g., | |
| // "alpn=h2,h3 dohpath=/dns-query{?dns}") to RFC 9460 Section 2.2 SVCB wire | |
| // format. Returns std::nullopt if parsing fails. | |
| QUICHE_EXPORT std::optional<std::string> EncodeSvcParams( | |
| absl::string_view presentation); | |
| // Decodes service parameters from RFC 9460 Section 2.2 SVCB wire format to | |
| // DNS presentation format. Returns std::nullopt if parsing fails. | |
| QUICHE_EXPORT std::optional<std::string> DecodeSvcParams( | |
| absl::string_view wire_format); | |
| // Represents a DNS Configuration structure (Section 3.3) describing a set of | |
| // nameservers responsible for resolving specific internal and search domains. | |
| struct QUICHE_EXPORT DnsConfiguration { | |
| // Series of Nameserver structures representing how to reach the resolvers. | |
| std::vector<DnsNameserver> nameservers; | |
| // Series of Domain structures representing internal domain names and their | |
| // subdomains that the nameservers are authoritative for. An empty string | |
| // indicates the DNS root (authoritative for all domain names). | |
| std::vector<DnsDomain> internal_domains; | |
| // Series of Domain structures representing DNS search domains. | |
| std::vector<DnsDomain> search_domains; | |
| bool operator==(const DnsConfiguration& other) const { | |
| return nameservers == other.nameservers && | |
| internal_domains == other.internal_domains && | |
| search_domains == other.search_domains; | |
| } | |
| std::string ToString() const; | |
| }; | |
| // Represents a DNS_ASSIGN capsule (Section 3.4) allowing an endpoint to send | |
| // one or more DNS configurations to its peer over a CONNECT-IP stream. | |
| struct QUICHE_EXPORT DnsAssignCapsule { | |
| // Multiple DNS configurations may be included if different DNS servers are | |
| // responsible for separate internal domains. | |
| std::vector<DnsConfiguration> dns_configurations; | |
| bool operator==(const DnsAssignCapsule& other) const { | |
| return dns_configurations == other.dns_configurations; | |
| } | |
| std::string ToString() const; | |
| }; | |
| // Represents an individual NAT64 prefix (Section 4.1) used for IPv6/IPv4 | |
| // address synthesis in IPv6-only environments. | |
| struct QUICHE_EXPORT Pref64Prefix { | |
| // Length of the NAT64 prefix in bits. Valid values are 32, 40, 48, 56, 64, | |
| // and 96. | |
| uint8_t prefix_length = 0; | |
| // The highest 96 bits (12 bytes) of the IPv6 prefix. Stored as an IPv6 | |
| // address where the trailing 32 bits are zeroed. | |
| QuicIpAddress prefix; | |
| bool operator==(const Pref64Prefix& other) const { | |
| return prefix_length == other.prefix_length && prefix == other.prefix; | |
| } | |
| std::string ToString() const; | |
| }; | |
| // Represents a PREF64 capsule (Section 4.1) conveying zero or more NAT64 | |
| // prefixes. An empty PREF64 capsule informs that NAT64 prefixes are not | |
| // available. | |
| struct QUICHE_EXPORT Pref64Capsule { | |
| std::vector<Pref64Prefix> nat64_prefixes; | |
| bool operator==(const Pref64Capsule& other) const { | |
| return nat64_prefixes == other.nat64_prefixes; | |
| } | |
| std::string ToString() const; | |
| }; | |
| // Parses the payload of a DNS_ASSIGN capsule into |capsule|. | |
| // Returns true if parsing succeeds, false otherwise (e.g., malformed format, | |
| // priority is 0, or trailing bytes remain). | |
| QUICHE_EXPORT bool ParseDnsAssignCapsulePayload(absl::string_view payload, | |
| DnsAssignCapsule* capsule); | |
| // Parses the payload of a PREF64 capsule into |capsule|. | |
| // Returns true on success, false if malformed (e.g., payload length is not a | |
| // multiple of 13 bytes or prefix_length is invalid). | |
| QUICHE_EXPORT bool ParsePref64CapsulePayload(absl::string_view payload, | |
| Pref64Capsule* capsule); | |
| // Serializes the payload of |capsule| into wire format bytes for a DNS_ASSIGN | |
| // capsule. | |
| QUICHE_EXPORT std::string SerializeDnsAssignCapsulePayload( | |
| const DnsAssignCapsule& capsule); | |
| // Serializes the payload of |capsule| into wire format bytes for a PREF64 | |
| // capsule. | |
| QUICHE_EXPORT std::string SerializePref64CapsulePayload( | |
| const Pref64Capsule& capsule); | |
| // Serializes and writes a DNS_ASSIGN capsule to |stream|. |fin| indicates | |
| // whether to close the stream write side after sending. | |
| QUICHE_EXPORT void WriteDnsAssignCapsule(QuicSpdyStream* stream, | |
| const DnsAssignCapsule& capsule, | |
| bool fin = false); | |
| // Serializes and writes a PREF64 capsule to |stream|. |fin| indicates whether | |
| // to close the stream write side after sending. | |
| QUICHE_EXPORT void WritePref64Capsule(QuicSpdyStream* stream, | |
| const Pref64Capsule& capsule, | |
| bool fin = false); | |
| } // namespace quic | |
| #endif // QUICHE_QUIC_MASQUE_MASQUE_UTILS_H_ |