| /** @file |
| |
| HTTP configuration support. |
| |
| @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 "tscore/ink_inet.h" |
| #include "tscore/ink_resolver.h" |
| #include "ts/apidefs.h" |
| #include "ts/apidefs.h" |
| #include "tscore/ink_assert.h" |
| #include "tscore/IpMap.h" |
| #include "tscore/MemArena.h" |
| #include <algorithm> |
| #include <array> |
| |
| /// Load default inbound IP addresses from the configuration file. |
| void RecHttpLoadIp(const char *name, ///< Name of value in configuration file. |
| IpAddr &ip4, ///< [out] IPv4 address. |
| IpAddr &ip6 ///< [out] Ipv6 address. |
| ); |
| |
| /// Load up an IpMap with IP addresses from the configuration file. |
| void RecHttpLoadIpMap(const char *name, ///< Name of value in configuration file. |
| IpMap &ipmap ///< [out] IpMap. |
| ); |
| |
| /** A set of session protocols. |
| This depends on using @c SessionProtocolNameRegistry to get the indices. |
| */ |
| class SessionProtocolSet |
| { |
| using self_type = SessionProtocolSet; ///< Self reference type. |
| /// Storage for the set - a bit vector. |
| uint32_t m_bits = 0; |
| |
| public: |
| static constexpr int MAX = sizeof(m_bits) * CHAR_BIT; |
| |
| /// Default constructor. |
| /// Constructs and empty set. |
| SessionProtocolSet() = default; |
| |
| uint32_t |
| indexToMask(int idx) const |
| { |
| return 0 <= idx && idx < static_cast<int>(MAX) ? static_cast<uint32_t>(1) << idx : 0; |
| } |
| |
| /// Mark the protocol at @a idx as present. |
| void |
| markIn(int idx) |
| { |
| m_bits |= this->indexToMask(idx); |
| } |
| |
| /// Mark all the protocols in @a that as present in @a this. |
| void |
| markIn(self_type const &that) |
| { |
| m_bits |= that.m_bits; |
| } |
| /// Mark the protocol at a idx as not present. |
| void |
| markOut(int idx) |
| { |
| m_bits &= ~this->indexToMask(idx); |
| } |
| /// Mark the protocols in @a that as not in @a this. |
| void |
| markOut(self_type const &that) |
| { |
| m_bits &= ~(that.m_bits); |
| } |
| /// Test if a protocol is in the set. |
| bool |
| contains(int idx) const |
| { |
| return 0 != (m_bits & this->indexToMask(idx)); |
| } |
| /// Test if all the protocols in @a that are in @a this protocol set. |
| bool |
| contains(self_type const &that) const |
| { |
| return that.m_bits == (that.m_bits & m_bits); |
| } |
| /// Mark all possible protocols. |
| void |
| markAllIn() |
| { |
| m_bits = ~static_cast<uint32_t>(0); |
| } |
| /// Clear all protocols. |
| void |
| markAllOut() |
| { |
| m_bits = 0; |
| } |
| |
| /// Check for intersection. |
| bool |
| intersects(self_type const &that) |
| { |
| return 0 != (m_bits & that.m_bits); |
| } |
| |
| /// Check for empty set. |
| bool |
| isEmpty() const |
| { |
| return m_bits == 0; |
| } |
| |
| /// Equality (identical sets). |
| bool |
| operator==(self_type const &that) const |
| { |
| return m_bits == that.m_bits; |
| } |
| }; |
| |
| // Predefined sets of protocols, useful for configuration. |
| extern SessionProtocolSet HTTP_PROTOCOL_SET; |
| extern SessionProtocolSet HTTP2_PROTOCOL_SET; |
| extern SessionProtocolSet DEFAULT_NON_TLS_SESSION_PROTOCOL_SET; |
| extern SessionProtocolSet DEFAULT_TLS_SESSION_PROTOCOL_SET; |
| extern SessionProtocolSet DEFAULT_QUIC_SESSION_PROTOCOL_SET; |
| |
| const char *RecNormalizeProtoTag(const char *tag); |
| |
| /** Registered session protocol names. |
| |
| We do this to avoid lots of string compares. By normalizing the string names we can just compare |
| their indices in this table. |
| |
| @internal To simplify the implementation we limit the maximum number of strings to 32. That will |
| be sufficient for the foreseeable future. We can come back to this if it ever becomes a problem. |
| |
| @internal Because we have so few strings we just use a linear search. If the size gets much |
| larger we should consider doing something more clever. |
| |
| @internal This supports providing constant strings because those strings are exported to the |
| C API and this logic @b must return exactly those pointers. |
| */ |
| class SessionProtocolNameRegistry |
| { |
| public: |
| static int constexpr MAX = SessionProtocolSet::MAX; ///< Maximum # of registered names. |
| static int constexpr INVALID = -1; ///< Normalized invalid index value. |
| |
| static std::string_view convert_openssl_alpn_wire_format(int index); |
| |
| using TextView = ts::TextView; |
| |
| /// Default constructor. |
| /// Creates empty registry with no names. |
| SessionProtocolNameRegistry() = default; |
| |
| /** Get the index for @a name, registering it if needed. |
| The name is copied internally. |
| @return The index for the registered @a name. |
| */ |
| int toIndex(TextView name); |
| |
| /** Get the index for @a name, registering it if needed. |
| The caller @b guarantees @a name is persistent and immutable. |
| @return The index for the registered @a name. |
| */ |
| int toIndexConst(TextView name); |
| |
| /** Convert a @a name to an index. |
| @return The index for @a name or @c INVALID if it is not registered. |
| */ |
| int indexFor(TextView name) const; |
| |
| /** Convert an @a index to the corresponding name. |
| @return A pointer to the name or @c nullptr if the index isn't registered. |
| */ |
| TextView nameFor(int index) const; |
| |
| /** Convert an @a index to the corresponding name in OpenSSL ALPN wire format. |
| |
| OpenSSL ALPN wire format (vector of non-empty, 8-bit length-prefixed, byte strings) |
| https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_alpn_protos.html |
| |
| @return A pointer to the name or @c nullptr if the index isn't registered. |
| */ |
| static TextView wireNameFor(int index); |
| |
| /// Mark protocols as present in @a sp_set based on the names in @a value. |
| /// The names can be separated by ;/|,: and space. |
| /// @internal This is separated out to make it easy to access from the plugin API |
| /// implementation. |
| void markIn(const char *value, SessionProtocolSet &sp_set); |
| |
| protected: |
| int m_n = 0; ///< Index of first unused slot. |
| std::array<TextView, MAX> m_names; |
| ts::MemArena m_arena; ///< Storage for non-constant strings. |
| }; |
| |
| extern SessionProtocolNameRegistry globalSessionProtocolNameRegistry; |
| |
| /** Description of an proxy port. |
| |
| This consolidates the options needed for proxy ports, both data |
| and parsing. It provides a static global set of ports for |
| convenience although it can be used with an externally provided |
| set. |
| |
| Options are described by a colon separated list of keywords |
| without spaces. The options are applied in left to right order. If |
| options do not conflict the order is irrelevant. |
| |
| IPv6 addresses must be enclosed by brackets. Unfortunate but colon is |
| so overloaded there's no other option. |
| */ |
| struct HttpProxyPort { |
| private: |
| typedef HttpProxyPort self; ///< Self reference type. |
| public: |
| /// Explicitly supported collection of proxy ports. |
| typedef std::vector<self> Group; |
| |
| /// Type of transport on the connection. |
| enum TransportType { |
| TRANSPORT_NONE = 0, ///< Unspecified / uninitialized |
| TRANSPORT_DEFAULT, ///< Default (normal HTTP). |
| TRANSPORT_COMPRESSED, ///< Compressed HTTP. |
| TRANSPORT_BLIND_TUNNEL, ///< Blind tunnel (no processing). |
| TRANSPORT_SSL, ///< SSL connection. |
| TRANSPORT_PLUGIN, /// < Protocol plugin connection |
| TRANSPORT_QUIC, ///< SSL connection. |
| }; |
| |
| int m_fd; ///< Pre-opened file descriptor if present. |
| TransportType m_type = TRANSPORT_DEFAULT; ///< Type of connection. |
| in_port_t m_port = 0; ///< Port on which to listen. |
| uint8_t m_family = AF_INET; ///< IP address family. |
| /// True if proxy protocol is required on incoming requests. |
| bool m_proxy_protocol = false; |
| /// True if inbound connects (from client) are transparent. |
| bool m_inbound_transparent_p = false; |
| /// True if outbound connections (to origin servers) are transparent. |
| bool m_outbound_transparent_p = false; |
| // True if transparent pass-through is enabled on this port. |
| bool m_transparent_passthrough = false; |
| /// True if MPTCP is enabled on this port. |
| bool m_mptcp = false; |
| /// Local address for inbound connections (listen address). |
| IpAddr m_inbound_ip; |
| /// Local address for outbound connections (to origin server). |
| IpAddr m_outbound_ip4; |
| /// Local address for outbound connections (to origin server). |
| IpAddr m_outbound_ip6; |
| /// Ordered preference for DNS resolution family ( @c FamilyPrefence ) |
| /// A value of @c PREFER_NONE indicates that entry and subsequent ones |
| /// are invalid. |
| HostResPreferenceOrder m_host_res_preference; |
| /// Static preference list that is the default value. |
| static HostResPreferenceOrder const DEFAULT_HOST_RES_PREFERENCE; |
| /// Enabled session transports for this port. |
| SessionProtocolSet m_session_protocol_preference; |
| |
| /// Default constructor. |
| HttpProxyPort(); |
| |
| /** Select the local outbound address object. |
| |
| @return The IP address for @a family |
| */ |
| IpAddr &outboundIp(uint16_t family ///< IP address family. |
| ); |
| |
| /// Check for SSL port. |
| bool isSSL() const; |
| |
| /// Check for QUIC port. |
| bool isQUIC() const; |
| |
| /// Check for SSL port. |
| bool isPlugin() const; |
| |
| /// Process options text. |
| /// @a opts should not contain any whitespace, only the option string. |
| /// This object's internal state is updated as specified by @a opts. |
| /// @return @c true if a port option was successfully processed, @c false otherwise. |
| bool processOptions(const char *opts ///< String containing the options. |
| ); |
| |
| /** Global instance. |
| |
| This is provided because most of the work with this data is used as a singleton |
| and it's handy to encapsulate it here. |
| */ |
| static std::vector<self> &global(); |
| |
| /// Check for SSL ports. |
| /// @return @c true if any port in @a ports is an SSL port. |
| static bool hasSSL(Group const &ports ///< Ports to check. |
| ); |
| |
| /// Check for SSL ports. |
| /// @return @c true if any global port is an SSL port. |
| static bool hasSSL(); |
| |
| /// Check for QUIC ports. |
| /// @return @c true if any port in @a ports is an QUIC port. |
| static bool hasQUIC(Group const &ports ///< Ports to check. |
| ); |
| |
| /// Check for QUIC ports. |
| /// @return @c true if any global port is an QUIC port. |
| static bool hasQUIC(); |
| |
| /** Load all relevant configuration data. |
| |
| This is hardwired to look up the appropriate values in the |
| configuration files. It clears @a ports and then loads all found |
| values in to it. |
| |
| @return @c true if at least one valid port description was |
| found, @c false if none. |
| */ |
| static bool loadConfig(std::vector<self> &ports ///< Destination for found port data. |
| ); |
| |
| /** Load all relevant configuration data into the global ports. |
| |
| @return @c true if at least one valid port description was |
| found, @c false if none. |
| */ |
| static bool loadConfig(); |
| |
| /** Load ports from a value string. |
| |
| Load ports from single string with port descriptors. Ports |
| found are added to @a ports. @a value may safely be @c nullptr or empty. |
| |
| @note This is used primarily internally but is available if needed. |
| @return @c true if a valid port was found, @c false if none. |
| */ |
| static bool loadValue(std::vector<self> &ports, ///< Destination for found port data. |
| const char *value ///< Source port data. |
| ); |
| |
| /** Load ports from a value string into the global ports. |
| |
| Load ports from single string of port descriptors into the |
| global set of ports. @a value may safely be @c nullptr or empty. |
| |
| @return @c true if a valid port was found, @c false if none. |
| */ |
| static bool loadValue(const char *value ///< Source port data. |
| ); |
| |
| /// Load default value if @a ports is empty. |
| /// @return @c true if the default was needed / loaded. |
| static bool loadDefaultIfEmpty(std::vector<self> &ports ///< Load target. |
| ); |
| |
| /// Load default value into the global set if it is empty. |
| /// @return @c true if the default was needed / loaded. |
| static bool loadDefaultIfEmpty(); |
| |
| /** Find an HTTP port in @a ports. |
| If @a family is specified then only ports for that family |
| are checked. |
| @return The port if found, @c nullptr if not. |
| */ |
| static const self *findHttp(Group const &ports, ///< Group to search. |
| uint16_t family = AF_UNSPEC ///< Desired address family. |
| ); |
| |
| /** Find an HTTP port in the global ports. |
| If @a family is specified then only ports for that family |
| are checked. |
| @return The port if found, @c nullptr if not. |
| */ |
| static const self *findHttp(uint16_t family = AF_UNSPEC); |
| |
| /** Create text description to be used for inter-process access. |
| Prints the file descriptor and then any options. |
| |
| @return The number of characters used for the description. |
| */ |
| int print(char *out, ///< Output string. |
| size_t n ///< Maximum output length. |
| ); |
| |
| static const char *const PORTS_CONFIG_NAME; ///< New unified port descriptor. |
| |
| /// Default value if no other values can be found. |
| static const char *const DEFAULT_VALUE; |
| |
| // Keywords (lower case versions, but compares should be case insensitive) |
| static const char *const OPT_FD_PREFIX; ///< Prefix for file descriptor value. |
| static const char *const OPT_OUTBOUND_IP_PREFIX; ///< Prefix for inbound IP address. |
| static const char *const OPT_INBOUND_IP_PREFIX; ///< Prefix for outbound IP address. |
| static const char *const OPT_IPV6; ///< IPv6. |
| static const char *const OPT_IPV4; ///< IPv4 |
| static const char *const OPT_TRANSPARENT_INBOUND; ///< Inbound transparent. |
| static const char *const OPT_TRANSPARENT_OUTBOUND; ///< Outbound transparent. |
| static const char *const OPT_TRANSPARENT_FULL; ///< Full transparency. |
| static const char *const OPT_TRANSPARENT_PASSTHROUGH; ///< Pass-through non-HTTP. |
| static const char *const OPT_SSL; ///< SSL (experimental) |
| static const char *const OPT_QUIC; ///< QUIC (experimental) |
| static const char *const OPT_PROXY_PROTO; ///< Proxy Protocol |
| static const char *const OPT_PLUGIN; ///< Protocol Plugin handle (experimental) |
| static const char *const OPT_BLIND_TUNNEL; ///< Blind tunnel. |
| static const char *const OPT_COMPRESSED; ///< Compressed. |
| static const char *const OPT_HOST_RES_PREFIX; ///< Set DNS family preference. |
| static const char *const OPT_PROTO_PREFIX; ///< Transport layer protocols. |
| static const char *const OPT_MPTCP; ///< MPTCP. |
| |
| static std::vector<self> &m_global; ///< Global ("default") data. |
| |
| protected: |
| /// Process @a value for DNS resolution family preferences. |
| void processFamilyPreference(const char *value); |
| /// Process @a value for session protocol preferences. |
| void processSessionProtocolPreference(const char *value); |
| |
| /** Check a prefix option and find the value. |
| @return The address of the start of the value, or @c nullptr if the prefix doesn't match. |
| */ |
| |
| const char *checkPrefix(char const *src ///< Input text |
| , |
| const char *prefix ///< Keyword prefix |
| , |
| size_t prefix_len ///< Length of keyword prefix. |
| ); |
| }; |
| |
| inline bool |
| HttpProxyPort::isSSL() const |
| { |
| return TRANSPORT_SSL == m_type; |
| } |
| inline bool |
| HttpProxyPort::isQUIC() const |
| { |
| return TRANSPORT_QUIC == m_type; |
| } |
| inline bool |
| HttpProxyPort::isPlugin() const |
| { |
| return TRANSPORT_PLUGIN == m_type; |
| } |
| |
| inline IpAddr & |
| HttpProxyPort::outboundIp(uint16_t family) |
| { |
| static IpAddr invalid; // dummy to make compiler happy about return. |
| if (AF_INET == family) |
| return m_outbound_ip4; |
| else if (AF_INET6 == family) |
| return m_outbound_ip6; |
| ink_release_assert(!"Invalid family for outbound address on proxy port."); |
| return invalid; // never happens but compiler insists. |
| } |
| |
| inline bool |
| HttpProxyPort::loadValue(const char *value) |
| { |
| return self::loadValue(m_global, value); |
| } |
| inline bool |
| HttpProxyPort::loadConfig() |
| { |
| return self::loadConfig(m_global); |
| } |
| inline bool |
| HttpProxyPort::loadDefaultIfEmpty() |
| { |
| return self::loadDefaultIfEmpty(m_global); |
| } |
| inline std::vector<HttpProxyPort> & |
| HttpProxyPort::global() |
| { |
| return m_global; |
| } |
| inline bool |
| HttpProxyPort::hasSSL() |
| { |
| return self::hasSSL(m_global); |
| } |
| inline bool |
| HttpProxyPort::hasQUIC() |
| { |
| return self::hasQUIC(m_global); |
| } |
| inline const HttpProxyPort * |
| HttpProxyPort::findHttp(uint16_t family) |
| { |
| return self::findHttp(m_global, family); |
| } |
| |
| /** Session Protocol initialization. |
| This must be called before any proxy port parsing is done. |
| */ |
| extern void ts_session_protocol_well_known_name_indices_init(); |