1 /** @file
2 
3   HTTP configuration support.
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22 */
23 
24 #include <records/I_RecCore.h>
25 #include <records/I_RecHttp.h>
26 #include "tscore/ink_defs.h"
27 #include "tscore/TextBuffer.h"
28 #include "tscore/Tokenizer.h"
29 #include <strings.h>
30 #include "tscore/ink_inet.h"
31 #include <string_view>
32 #include <unordered_set>
33 #include <tscore/IpMapConf.h>
34 
35 SessionProtocolNameRegistry globalSessionProtocolNameRegistry;
36 
37 /* Protocol session well-known protocol names.
38    These are also used for NPN setup.
39 */
40 
41 const char *const TS_ALPN_PROTOCOL_HTTP_0_9      = IP_PROTO_TAG_HTTP_0_9.data();
42 const char *const TS_ALPN_PROTOCOL_HTTP_1_0      = IP_PROTO_TAG_HTTP_1_0.data();
43 const char *const TS_ALPN_PROTOCOL_HTTP_1_1      = IP_PROTO_TAG_HTTP_1_1.data();
44 const char *const TS_ALPN_PROTOCOL_HTTP_2_0      = IP_PROTO_TAG_HTTP_2_0.data();
45 const char *const TS_ALPN_PROTOCOL_HTTP_3        = IP_PROTO_TAG_HTTP_3.data();
46 const char *const TS_ALPN_PROTOCOL_HTTP_QUIC     = IP_PROTO_TAG_HTTP_QUIC.data();
47 const char *const TS_ALPN_PROTOCOL_HTTP_3_D27    = IP_PROTO_TAG_HTTP_3_D27.data();
48 const char *const TS_ALPN_PROTOCOL_HTTP_QUIC_D27 = IP_PROTO_TAG_HTTP_QUIC_D27.data();
49 
50 const char *const TS_ALPN_PROTOCOL_GROUP_HTTP  = "http";
51 const char *const TS_ALPN_PROTOCOL_GROUP_HTTP2 = "http2";
52 
53 const char *const TS_PROTO_TAG_HTTP_1_0      = TS_ALPN_PROTOCOL_HTTP_1_0;
54 const char *const TS_PROTO_TAG_HTTP_1_1      = TS_ALPN_PROTOCOL_HTTP_1_1;
55 const char *const TS_PROTO_TAG_HTTP_2_0      = TS_ALPN_PROTOCOL_HTTP_2_0;
56 const char *const TS_PROTO_TAG_HTTP_3        = TS_ALPN_PROTOCOL_HTTP_3;
57 const char *const TS_PROTO_TAG_HTTP_QUIC     = TS_ALPN_PROTOCOL_HTTP_QUIC;
58 const char *const TS_PROTO_TAG_HTTP_3_D27    = TS_ALPN_PROTOCOL_HTTP_3_D27;
59 const char *const TS_PROTO_TAG_HTTP_QUIC_D27 = TS_ALPN_PROTOCOL_HTTP_QUIC_D27;
60 const char *const TS_PROTO_TAG_TLS_1_3       = IP_PROTO_TAG_TLS_1_3.data();
61 const char *const TS_PROTO_TAG_TLS_1_2       = IP_PROTO_TAG_TLS_1_2.data();
62 const char *const TS_PROTO_TAG_TLS_1_1       = IP_PROTO_TAG_TLS_1_1.data();
63 const char *const TS_PROTO_TAG_TLS_1_0       = IP_PROTO_TAG_TLS_1_0.data();
64 const char *const TS_PROTO_TAG_TCP           = IP_PROTO_TAG_TCP.data();
65 const char *const TS_PROTO_TAG_UDP           = IP_PROTO_TAG_UDP.data();
66 const char *const TS_PROTO_TAG_IPV4          = IP_PROTO_TAG_IPV4.data();
67 const char *const TS_PROTO_TAG_IPV6          = IP_PROTO_TAG_IPV6.data();
68 
69 std::unordered_set<std::string_view> TSProtoTags;
70 
71 // Precomputed indices for ease of use.
72 int TS_ALPN_PROTOCOL_INDEX_HTTP_0_9      = SessionProtocolNameRegistry::INVALID;
73 int TS_ALPN_PROTOCOL_INDEX_HTTP_1_0      = SessionProtocolNameRegistry::INVALID;
74 int TS_ALPN_PROTOCOL_INDEX_HTTP_1_1      = SessionProtocolNameRegistry::INVALID;
75 int TS_ALPN_PROTOCOL_INDEX_HTTP_2_0      = SessionProtocolNameRegistry::INVALID;
76 int TS_ALPN_PROTOCOL_INDEX_HTTP_3        = SessionProtocolNameRegistry::INVALID;
77 int TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC     = SessionProtocolNameRegistry::INVALID;
78 int TS_ALPN_PROTOCOL_INDEX_HTTP_3_D27    = SessionProtocolNameRegistry::INVALID;
79 int TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC_D27 = SessionProtocolNameRegistry::INVALID;
80 
81 // Predefined protocol sets for ease of use.
82 SessionProtocolSet HTTP_PROTOCOL_SET;
83 SessionProtocolSet HTTP2_PROTOCOL_SET;
84 SessionProtocolSet DEFAULT_NON_TLS_SESSION_PROTOCOL_SET;
85 SessionProtocolSet DEFAULT_TLS_SESSION_PROTOCOL_SET;
86 SessionProtocolSet DEFAULT_QUIC_SESSION_PROTOCOL_SET;
87 
88 static bool
mptcp_supported()89 mptcp_supported()
90 {
91   ats_scoped_fd fd(::open("/proc/sys/net/mptcp/mptcp_enabled", O_RDONLY));
92   int value = 0;
93 
94   if (fd) {
95     TextBuffer buffer(16);
96 
97     buffer.slurp(fd.get());
98     value = atoi(buffer.bufPtr());
99   }
100 
101   return value != 0;
102 }
103 
104 void
RecHttpLoadIp(const char * value_name,IpAddr & ip4,IpAddr & ip6)105 RecHttpLoadIp(const char *value_name, IpAddr &ip4, IpAddr &ip6)
106 {
107   char value[1024];
108   ip4.invalidate();
109   ip6.invalidate();
110   if (REC_ERR_OKAY == RecGetRecordString(value_name, value, sizeof(value))) {
111     Tokenizer tokens(", ");
112     int n_addrs = tokens.Initialize(value);
113     for (int i = 0; i < n_addrs; ++i) {
114       const char *host = tokens[i];
115       IpEndpoint tmp4, tmp6;
116       // For backwards compatibility we need to support the use of host names
117       // for the address to bind.
118       if (0 == ats_ip_getbestaddrinfo(host, &tmp4, &tmp6)) {
119         if (ats_is_ip4(&tmp4)) {
120           if (!ip4.isValid()) {
121             ip4 = tmp4;
122           } else {
123             Warning("'%s' specifies more than one IPv4 address, ignoring %s.", value_name, host);
124           }
125         }
126         if (ats_is_ip6(&tmp6)) {
127           if (!ip6.isValid()) {
128             ip6 = tmp6;
129           } else {
130             Warning("'%s' specifies more than one IPv6 address, ignoring %s.", value_name, host);
131           }
132         }
133       } else {
134         Warning("'%s' has an value '%s' that is not recognized as an IP address, ignored.", value_name, host);
135       }
136     }
137   }
138 }
139 
140 void
RecHttpLoadIpMap(const char * value_name,IpMap & ipmap)141 RecHttpLoadIpMap(const char *value_name, IpMap &ipmap)
142 {
143   char value[1024];
144   IpAddr laddr;
145   IpAddr raddr;
146   void *payload = nullptr;
147 
148   if (REC_ERR_OKAY == RecGetRecordString(value_name, value, sizeof(value))) {
149     Debug("config", "RecHttpLoadIpMap: parsing the name [%s] and value [%s] to an IpMap", value_name, value);
150     Tokenizer tokens(", ");
151     int n_addrs = tokens.Initialize(value);
152     for (int i = 0; i < n_addrs; ++i) {
153       const char *val = tokens[i];
154 
155       Debug("config", "RecHttpLoadIpMap: marking the value [%s] to an IpMap entry", val);
156       if (0 == ats_ip_range_parse(val, laddr, raddr)) {
157         ipmap.fill(laddr, raddr, payload);
158       }
159     }
160   }
161   Debug("config", "RecHttpLoadIpMap: parsed %zu IpMap entries", ipmap.count());
162 }
163 
164 const char *const HttpProxyPort::DEFAULT_VALUE = "8080";
165 
166 const char *const HttpProxyPort::PORTS_CONFIG_NAME = "proxy.config.http.server_ports";
167 
168 // "_PREFIX" means the option contains additional data.
169 // Each has a corresponding _LEN value that is the length of the option text.
170 // Options without _PREFIX are just flags with no additional data.
171 
172 const char *const HttpProxyPort::OPT_FD_PREFIX          = "fd";
173 const char *const HttpProxyPort::OPT_OUTBOUND_IP_PREFIX = "ip-out";
174 const char *const HttpProxyPort::OPT_INBOUND_IP_PREFIX  = "ip-in";
175 const char *const HttpProxyPort::OPT_HOST_RES_PREFIX    = "ip-resolve";
176 const char *const HttpProxyPort::OPT_PROTO_PREFIX       = "proto";
177 
178 const char *const HttpProxyPort::OPT_IPV6                    = "ipv6";
179 const char *const HttpProxyPort::OPT_IPV4                    = "ipv4";
180 const char *const HttpProxyPort::OPT_TRANSPARENT_INBOUND     = "tr-in";
181 const char *const HttpProxyPort::OPT_TRANSPARENT_OUTBOUND    = "tr-out";
182 const char *const HttpProxyPort::OPT_TRANSPARENT_FULL        = "tr-full";
183 const char *const HttpProxyPort::OPT_TRANSPARENT_PASSTHROUGH = "tr-pass";
184 const char *const HttpProxyPort::OPT_SSL                     = "ssl";
185 const char *const HttpProxyPort::OPT_PROXY_PROTO             = "pp";
186 const char *const HttpProxyPort::OPT_PLUGIN                  = "plugin";
187 const char *const HttpProxyPort::OPT_BLIND_TUNNEL            = "blind";
188 const char *const HttpProxyPort::OPT_COMPRESSED              = "compressed";
189 const char *const HttpProxyPort::OPT_MPTCP                   = "mptcp";
190 const char *const HttpProxyPort::OPT_QUIC                    = "quic";
191 
192 // File local constants.
193 namespace
194 {
195 // Length values for _PREFIX options.
196 size_t const OPT_FD_PREFIX_LEN          = strlen(HttpProxyPort::OPT_FD_PREFIX);
197 size_t const OPT_OUTBOUND_IP_PREFIX_LEN = strlen(HttpProxyPort::OPT_OUTBOUND_IP_PREFIX);
198 size_t const OPT_INBOUND_IP_PREFIX_LEN  = strlen(HttpProxyPort::OPT_INBOUND_IP_PREFIX);
199 size_t const OPT_HOST_RES_PREFIX_LEN    = strlen(HttpProxyPort::OPT_HOST_RES_PREFIX);
200 size_t const OPT_PROTO_PREFIX_LEN       = strlen(HttpProxyPort::OPT_PROTO_PREFIX);
201 
202 constexpr std::string_view TS_ALPN_PROTO_ID_OPENSSL_HTTP_0_9("\x8http/0.9");
203 constexpr std::string_view TS_ALPN_PROTO_ID_OPENSSL_HTTP_1_0("\x8http/1.0");
204 constexpr std::string_view TS_ALPN_PROTO_ID_OPENSSL_HTTP_1_1("\x8http/1.1");
205 constexpr std::string_view TS_ALPN_PROTO_ID_OPENSSL_HTTP_2("\x2h2");
206 constexpr std::string_view TS_ALPN_PROTO_ID_OPENSSL_HTTP_3("\x2h3");
207 } // namespace
208 
209 namespace
210 {
211 // Solaris work around. On that OS the compiler will not let me use an
212 // instantiated instance of Vec<self> inside the class, even if
213 // static. So we have to declare it elsewhere and then import via
214 // reference. Might be a problem with Vec<> creating a fixed array
215 // rather than allocating on first use (compared to std::vector<>).
216 HttpProxyPort::Group GLOBAL_DATA;
217 } // namespace
218 HttpProxyPort::Group &HttpProxyPort::m_global = GLOBAL_DATA;
219 
HttpProxyPort()220 HttpProxyPort::HttpProxyPort() : m_fd(ts::NO_FD)
221 
222 {
223   m_host_res_preference = host_res_default_preference_order;
224 }
225 
226 bool
hasSSL(Group const & ports)227 HttpProxyPort::hasSSL(Group const &ports)
228 {
229   return std::any_of(ports.begin(), ports.end(), [](HttpProxyPort const &port) { return port.isSSL(); });
230 }
231 
232 bool
hasQUIC(Group const & ports)233 HttpProxyPort::hasQUIC(Group const &ports)
234 {
235   bool zret = false;
236   for (int i = 0, n = ports.size(); i < n && !zret; ++i) {
237     if (ports[i].isQUIC()) {
238       zret = true;
239     }
240   }
241   return zret;
242 }
243 
244 const HttpProxyPort *
findHttp(Group const & ports,uint16_t family)245 HttpProxyPort::findHttp(Group const &ports, uint16_t family)
246 {
247   bool check_family_p = ats_is_ip(family);
248   const self *zret    = nullptr;
249   for (int i = 0, n = ports.size(); i < n && !zret; ++i) {
250     const self &p = ports[i];
251     if (p.m_port &&                               // has a valid port
252         TRANSPORT_DEFAULT == p.m_type &&          // is normal HTTP
253         (!check_family_p || p.m_family == family) // right address family
254     ) {
255       zret = &p;
256     };
257   }
258   return zret;
259 }
260 
261 const char *
checkPrefix(const char * src,char const * prefix,size_t prefix_len)262 HttpProxyPort::checkPrefix(const char *src, char const *prefix, size_t prefix_len)
263 {
264   const char *zret = nullptr;
265   if (0 == strncasecmp(prefix, src, prefix_len)) {
266     src += prefix_len;
267     if ('-' == *src || '=' == *src) {
268       ++src; // permit optional '-' or '='
269     }
270     zret = src;
271   }
272   return zret;
273 }
274 
275 bool
loadConfig(std::vector<self> & entries)276 HttpProxyPort::loadConfig(std::vector<self> &entries)
277 {
278   char *text;
279   bool found_p;
280 
281   text = REC_readString(PORTS_CONFIG_NAME, &found_p);
282   if (found_p) {
283     self::loadValue(entries, text);
284   }
285   ats_free(text);
286 
287   return 0 < entries.size();
288 }
289 
290 bool
loadDefaultIfEmpty(Group & ports)291 HttpProxyPort::loadDefaultIfEmpty(Group &ports)
292 {
293   if (0 == ports.size()) {
294     self::loadValue(ports, DEFAULT_VALUE);
295   }
296 
297   return 0 < ports.size();
298 }
299 
300 bool
loadValue(std::vector<self> & ports,const char * text)301 HttpProxyPort::loadValue(std::vector<self> &ports, const char *text)
302 {
303   unsigned old_port_length = ports.size(); // remember this.
304   if (text && *text) {
305     Tokenizer tokens(", ");
306     int n_ports = tokens.Initialize(text);
307     if (n_ports > 0) {
308       for (int p = 0; p < n_ports; ++p) {
309         const char *elt = tokens[p];
310         HttpProxyPort entry;
311         if (entry.processOptions(elt)) {
312           ports.push_back(entry);
313         } else {
314           Warning("No valid definition was found in proxy port configuration element '%s'", elt);
315         }
316       }
317     }
318   }
319   return ports.size() > old_port_length; // we added at least one port.
320 }
321 
322 bool
processOptions(const char * opts)323 HttpProxyPort::processOptions(const char *opts)
324 {
325   bool zret           = false; // found a port?
326   bool af_set_p       = false; // AF explicitly specified?
327   bool host_res_set_p = false; // Host resolution order set explicitly?
328   bool sp_set_p       = false; // Session protocol set explicitly?
329   bool bracket_p      = false; // found an open bracket in the input?
330   const char *value;           // Temp holder for value of a prefix option.
331   IpAddr ip;                   // temp for loading IP addresses.
332   std::vector<char *> values;  // Pointers to single option values.
333 
334   // Make a copy we can modify safely.
335   size_t opts_len = strlen(opts) + 1;
336   char *text      = static_cast<char *>(alloca(opts_len));
337   memcpy(text, opts, opts_len);
338 
339   // Split the copy in to tokens.
340   char *token = nullptr;
341   for (char *spot = text; *spot; ++spot) {
342     if (bracket_p) {
343       if (']' == *spot) {
344         bracket_p = false;
345       }
346     } else if (':' == *spot) {
347       *spot = 0;
348       token = nullptr;
349     } else {
350       if (!token) {
351         token = spot;
352         values.push_back(token);
353       }
354       if ('[' == *spot) {
355         bracket_p = true;
356       }
357     }
358   }
359   if (bracket_p) {
360     Warning("Invalid port descriptor '%s' - left bracket without closing right bracket", opts);
361     return zret;
362   }
363 
364   for (auto item : values) {
365     if (isdigit(item[0])) { // leading digit -> port value
366       char *ptr;
367       int port = strtoul(item, &ptr, 10);
368       if (ptr == item) {
369         // really, this shouldn't happen, since we checked for a leading digit.
370         Warning("Mangled port value '%s' in port configuration '%s'", item, opts);
371       } else if (port <= 0 || 65536 <= port) {
372         Warning("Port value '%s' out of range (1..65535) in port configuration '%s'", item, opts);
373       } else {
374         m_port = port;
375         zret   = true;
376       }
377     } else if (nullptr != (value = this->checkPrefix(item, OPT_FD_PREFIX, OPT_FD_PREFIX_LEN))) {
378       char *ptr; // tmp for syntax check.
379       int fd = strtoul(value, &ptr, 10);
380       if (ptr == value) {
381         Warning("Mangled file descriptor value '%s' in port descriptor '%s'", item, opts);
382       } else {
383         m_fd = fd;
384         zret = true;
385       }
386     } else if (nullptr != (value = this->checkPrefix(item, OPT_INBOUND_IP_PREFIX, OPT_INBOUND_IP_PREFIX_LEN))) {
387       if (0 == ip.load(value)) {
388         m_inbound_ip = ip;
389       } else {
390         Warning("Invalid IP address value '%s' in port descriptor '%s'", item, opts);
391       }
392     } else if (nullptr != (value = this->checkPrefix(item, OPT_OUTBOUND_IP_PREFIX, OPT_OUTBOUND_IP_PREFIX_LEN))) {
393       if (0 == ip.load(value)) {
394         this->outboundIp(ip.family()) = ip;
395       } else {
396         Warning("Invalid IP address value '%s' in port descriptor '%s'", item, opts);
397       }
398     } else if (0 == strcasecmp(OPT_COMPRESSED, item)) {
399       m_type = TRANSPORT_COMPRESSED;
400     } else if (0 == strcasecmp(OPT_BLIND_TUNNEL, item)) {
401       m_type = TRANSPORT_BLIND_TUNNEL;
402     } else if (0 == strcasecmp(OPT_IPV6, item)) {
403       m_family = AF_INET6;
404       af_set_p = true;
405     } else if (0 == strcasecmp(OPT_IPV4, item)) {
406       m_family = AF_INET;
407       af_set_p = true;
408     } else if (0 == strcasecmp(OPT_SSL, item)) {
409       m_type = TRANSPORT_SSL;
410 #if TS_USE_QUIC == 1
411     } else if (0 == strcasecmp(OPT_QUIC, item)) {
412       m_type = TRANSPORT_QUIC;
413 #endif
414     } else if (0 == strcasecmp(OPT_PLUGIN, item)) {
415       m_type = TRANSPORT_PLUGIN;
416     } else if (0 == strcasecmp(OPT_PROXY_PROTO, item)) {
417       m_proxy_protocol = true;
418     } else if (0 == strcasecmp(OPT_TRANSPARENT_INBOUND, item)) {
419 #if TS_USE_TPROXY
420       m_inbound_transparent_p = true;
421 #else
422       Warning("Transparency requested [%s] in port descriptor '%s' but TPROXY was not configured.", item, opts);
423 #endif
424     } else if (0 == strcasecmp(OPT_TRANSPARENT_OUTBOUND, item)) {
425 #if TS_USE_TPROXY
426       m_outbound_transparent_p = true;
427 #else
428       Warning("Transparency requested [%s] in port descriptor '%s' but TPROXY was not configured.", item, opts);
429 #endif
430     } else if (0 == strcasecmp(OPT_TRANSPARENT_FULL, item)) {
431 #if TS_USE_TPROXY
432       m_inbound_transparent_p  = true;
433       m_outbound_transparent_p = true;
434 #else
435       Warning("Transparency requested [%s] in port descriptor '%s' but TPROXY was not configured.", item, opts);
436 #endif
437     } else if (0 == strcasecmp(OPT_TRANSPARENT_PASSTHROUGH, item)) {
438 #if TS_USE_TPROXY
439       m_transparent_passthrough = true;
440 #else
441       Warning("Transparent pass-through requested [%s] in port descriptor '%s' but TPROXY was not configured.", item, opts);
442 #endif
443     } else if (0 == strcasecmp(OPT_MPTCP, item)) {
444       if (mptcp_supported()) {
445         m_mptcp = true;
446       } else {
447         Warning("Multipath TCP requested [%s] in port descriptor '%s' but it is not supported by this host.", item, opts);
448       }
449     } else if (nullptr != (value = this->checkPrefix(item, OPT_HOST_RES_PREFIX, OPT_HOST_RES_PREFIX_LEN))) {
450       this->processFamilyPreference(value);
451       host_res_set_p = true;
452     } else if (nullptr != (value = this->checkPrefix(item, OPT_PROTO_PREFIX, OPT_PROTO_PREFIX_LEN))) {
453       this->processSessionProtocolPreference(value);
454       sp_set_p = true;
455     } else {
456       Warning("Invalid option '%s' in proxy port descriptor '%s'", item, opts);
457     }
458   }
459 
460   bool in_ip_set_p = m_inbound_ip.isValid();
461 
462   if (af_set_p) {
463     if (in_ip_set_p && m_family != m_inbound_ip.family()) {
464       std::string_view iname{ats_ip_family_name(m_inbound_ip.family())};
465       std::string_view fname{ats_ip_family_name(m_family)};
466       Warning("Invalid port descriptor '%s' - the inbound address family [%.*s] is not the same type as the explicit family value "
467               "[%.*s]",
468               opts, static_cast<int>(iname.size()), iname.data(), static_cast<int>(fname.size()), fname.data());
469       zret = false;
470     }
471   } else if (in_ip_set_p) {
472     m_family = m_inbound_ip.family(); // set according to address.
473   }
474 
475   // If the port is outbound transparent only CLIENT host resolution is possible.
476   if (m_outbound_transparent_p) {
477     if (host_res_set_p &&
478         (m_host_res_preference[0] != HOST_RES_PREFER_CLIENT || m_host_res_preference[1] != HOST_RES_PREFER_NONE)) {
479       Warning("Outbound transparent port '%s' requires the IP address resolution ordering '%s,%s'. "
480               "This is set automatically and does not need to be set explicitly.",
481               opts, HOST_RES_PREFERENCE_STRING[HOST_RES_PREFER_CLIENT], HOST_RES_PREFERENCE_STRING[HOST_RES_PREFER_NONE]);
482     }
483     m_host_res_preference[0] = HOST_RES_PREFER_CLIENT;
484     m_host_res_preference[1] = HOST_RES_PREFER_NONE;
485   }
486 
487   // Transparent pass-through requires tr-in
488   if (m_transparent_passthrough && !m_inbound_transparent_p) {
489     Warning("Port descriptor '%s' has transparent pass-through enabled without inbound transparency, this will be ignored.", opts);
490     m_transparent_passthrough = false;
491   }
492 
493   // Set the default session protocols.
494   if (!sp_set_p) {
495     if (this->isSSL()) {
496       m_session_protocol_preference = DEFAULT_TLS_SESSION_PROTOCOL_SET;
497     } else if (this->isQUIC()) {
498       m_session_protocol_preference = DEFAULT_QUIC_SESSION_PROTOCOL_SET;
499     } else {
500       m_session_protocol_preference = DEFAULT_NON_TLS_SESSION_PROTOCOL_SET;
501     }
502   }
503 
504   return zret;
505 }
506 
507 void
processFamilyPreference(const char * value)508 HttpProxyPort::processFamilyPreference(const char *value)
509 {
510   parse_host_res_preference(value, m_host_res_preference);
511 }
512 
513 void
processSessionProtocolPreference(const char * value)514 HttpProxyPort::processSessionProtocolPreference(const char *value)
515 {
516   m_session_protocol_preference.markAllOut();
517   globalSessionProtocolNameRegistry.markIn(value, m_session_protocol_preference);
518 }
519 
520 void
markIn(const char * value,SessionProtocolSet & sp_set)521 SessionProtocolNameRegistry::markIn(const char *value, SessionProtocolSet &sp_set)
522 {
523   int n; // # of tokens
524   Tokenizer tokens(" ;|,:");
525 
526   n = tokens.Initialize(value);
527 
528   for (int i = 0; i < n; ++i) {
529     const char *elt = tokens[i];
530 
531     /// Check special cases
532     if (0 == strcasecmp(elt, TS_ALPN_PROTOCOL_GROUP_HTTP)) {
533       sp_set.markIn(HTTP_PROTOCOL_SET);
534     } else if (0 == strcasecmp(elt, TS_ALPN_PROTOCOL_GROUP_HTTP2)) {
535       sp_set.markIn(HTTP2_PROTOCOL_SET);
536     } else { // user defined - register and mark.
537       int idx = globalSessionProtocolNameRegistry.toIndex(TextView{elt, strlen(elt)});
538       sp_set.markIn(idx);
539     }
540   }
541 }
542 
543 int
print(char * out,size_t n)544 HttpProxyPort::print(char *out, size_t n)
545 {
546   size_t zret = 0; // # of chars printed so far.
547   ip_text_buffer ipb;
548   bool need_colon_p = false;
549 
550   if (m_inbound_ip.isValid()) {
551     zret += snprintf(out + zret, n - zret, "%s=[%s]", OPT_INBOUND_IP_PREFIX, m_inbound_ip.toString(ipb, sizeof(ipb)));
552     need_colon_p = true;
553   }
554   if (zret >= n) {
555     return n;
556   }
557 
558   if (m_outbound_ip4.isValid()) {
559     if (need_colon_p) {
560       out[zret++] = ':';
561     }
562     zret += snprintf(out + zret, n - zret, "%s=[%s]", OPT_OUTBOUND_IP_PREFIX, m_outbound_ip4.toString(ipb, sizeof(ipb)));
563     need_colon_p = true;
564   }
565   if (zret >= n) {
566     return n;
567   }
568 
569   if (m_outbound_ip6.isValid()) {
570     if (need_colon_p) {
571       out[zret++] = ':';
572     }
573     zret += snprintf(out + zret, n - zret, "%s=[%s]", OPT_OUTBOUND_IP_PREFIX, m_outbound_ip6.toString(ipb, sizeof(ipb)));
574     need_colon_p = true;
575   }
576   if (zret >= n) {
577     return n;
578   }
579 
580   if (0 != m_port) {
581     if (need_colon_p) {
582       out[zret++] = ':';
583     }
584     zret += snprintf(out + zret, n - zret, "%d", m_port);
585     need_colon_p = true;
586   }
587   if (zret >= n) {
588     return n;
589   }
590 
591   if (ts::NO_FD != m_fd) {
592     if (need_colon_p) {
593       out[zret++] = ':';
594     }
595     zret += snprintf(out + zret, n - zret, "fd=%d", m_fd);
596   }
597   if (zret >= n) {
598     return n;
599   }
600 
601   // After this point, all of these options require other options which we've already
602   // generated so all of them need a leading colon and we can stop checking for that.
603 
604   if (AF_INET6 == m_family) {
605     zret += snprintf(out + zret, n - zret, ":%s", OPT_IPV6);
606   }
607   if (zret >= n) {
608     return n;
609   }
610 
611   if (TRANSPORT_BLIND_TUNNEL == m_type) {
612     zret += snprintf(out + zret, n - zret, ":%s", OPT_BLIND_TUNNEL);
613   } else if (TRANSPORT_SSL == m_type) {
614     zret += snprintf(out + zret, n - zret, ":%s", OPT_SSL);
615   } else if (TRANSPORT_QUIC == m_type) {
616     zret += snprintf(out + zret, n - zret, ":%s", OPT_QUIC);
617   } else if (TRANSPORT_PLUGIN == m_type) {
618     zret += snprintf(out + zret, n - zret, ":%s", OPT_PLUGIN);
619   } else if (TRANSPORT_COMPRESSED == m_type) {
620     zret += snprintf(out + zret, n - zret, ":%s", OPT_COMPRESSED);
621   }
622   if (zret >= n) {
623     return n;
624   }
625 
626   if (m_proxy_protocol) {
627     zret += snprintf(out + zret, n - zret, ":%s", OPT_PROXY_PROTO);
628   }
629 
630   if (m_outbound_transparent_p && m_inbound_transparent_p) {
631     zret += snprintf(out + zret, n - zret, ":%s", OPT_TRANSPARENT_FULL);
632   } else if (m_inbound_transparent_p) {
633     zret += snprintf(out + zret, n - zret, ":%s", OPT_TRANSPARENT_INBOUND);
634   } else if (m_outbound_transparent_p) {
635     zret += snprintf(out + zret, n - zret, ":%s", OPT_TRANSPARENT_OUTBOUND);
636   }
637 
638   if (m_mptcp) {
639     zret += snprintf(out + zret, n - zret, ":%s", OPT_MPTCP);
640   }
641 
642   if (m_transparent_passthrough) {
643     zret += snprintf(out + zret, n - zret, ":%s", OPT_TRANSPARENT_PASSTHROUGH);
644   }
645 
646   /* Don't print the IP resolution preferences if the port is outbound
647    * transparent (which means the preference order is forced) or if
648    * the order is the same as the default.
649    */
650   if (!m_outbound_transparent_p && m_host_res_preference != host_res_default_preference_order) {
651     zret += snprintf(out + zret, n - zret, ":%s=", OPT_HOST_RES_PREFIX);
652     zret += ts_host_res_order_to_string(m_host_res_preference, out + zret, n - zret);
653   }
654 
655   // session protocol options - look for condensed options first
656   // first two cases are the defaults so if those match, print nothing.
657   SessionProtocolSet sp_set = m_session_protocol_preference; // need to modify so copy.
658   need_colon_p              = true;                          // for listing case, turned off if we do a special case.
659   if (sp_set == DEFAULT_NON_TLS_SESSION_PROTOCOL_SET && !this->isSSL()) {
660     sp_set.markOut(DEFAULT_NON_TLS_SESSION_PROTOCOL_SET);
661   } else if (sp_set == DEFAULT_TLS_SESSION_PROTOCOL_SET && this->isSSL()) {
662     sp_set.markOut(DEFAULT_TLS_SESSION_PROTOCOL_SET);
663   } else if (sp_set == DEFAULT_QUIC_SESSION_PROTOCOL_SET && this->isQUIC()) {
664     sp_set.markOut(DEFAULT_QUIC_SESSION_PROTOCOL_SET);
665   }
666 
667   // pull out groups.
668   if (sp_set.contains(HTTP_PROTOCOL_SET)) {
669     zret += snprintf(out + zret, n - zret, ":%s=%s", OPT_PROTO_PREFIX, TS_ALPN_PROTOCOL_GROUP_HTTP);
670     sp_set.markOut(HTTP_PROTOCOL_SET);
671     need_colon_p = false;
672   }
673   if (sp_set.contains(HTTP2_PROTOCOL_SET)) {
674     if (need_colon_p) {
675       zret += snprintf(out + zret, n - zret, ":%s=", OPT_PROTO_PREFIX);
676     } else {
677       out[zret++] = ';';
678     }
679     zret += snprintf(out + zret, n - zret, "%s", TS_ALPN_PROTOCOL_GROUP_HTTP2);
680     sp_set.markOut(HTTP2_PROTOCOL_SET);
681     need_colon_p = false;
682   }
683   // now enumerate what's left.
684   if (!sp_set.isEmpty()) {
685     if (need_colon_p) {
686       zret += snprintf(out + zret, n - zret, ":%s=", OPT_PROTO_PREFIX);
687     }
688     bool sep_p = !need_colon_p;
689     for (int k = 0; k < SessionProtocolSet::MAX; ++k) {
690       if (sp_set.contains(k)) {
691         auto name{globalSessionProtocolNameRegistry.nameFor(k)};
692         zret += snprintf(out + zret, n - zret, "%s%.*s", sep_p ? ";" : "", static_cast<int>(name.size()), name.data());
693         sep_p = true;
694       }
695     }
696   }
697 
698   return std::min(zret, n);
699 }
700 
701 void
ts_host_res_global_init()702 ts_host_res_global_init()
703 {
704   // Global configuration values.
705   host_res_default_preference_order = HOST_RES_DEFAULT_PREFERENCE_ORDER;
706   char *ip_resolve                  = REC_ConfigReadString("proxy.config.hostdb.ip_resolve");
707   if (ip_resolve) {
708     parse_host_res_preference(ip_resolve, host_res_default_preference_order);
709   }
710   ats_free(ip_resolve);
711 }
712 
713 // Whatever executable uses librecords must call this.
714 void
ts_session_protocol_well_known_name_indices_init()715 ts_session_protocol_well_known_name_indices_init()
716 {
717   // register all the well known protocols and get the indices set.
718   TS_ALPN_PROTOCOL_INDEX_HTTP_0_9   = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_0_9});
719   TS_ALPN_PROTOCOL_INDEX_HTTP_1_0   = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_1_0});
720   TS_ALPN_PROTOCOL_INDEX_HTTP_1_1   = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_1_1});
721   TS_ALPN_PROTOCOL_INDEX_HTTP_2_0   = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_2_0});
722   TS_ALPN_PROTOCOL_INDEX_HTTP_3     = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_3});
723   TS_ALPN_PROTOCOL_INDEX_HTTP_3_D27 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_3_D27});
724   TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC  = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_QUIC});
725   TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC_D27 =
726     globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_QUIC_D27});
727 
728   // Now do the predefined protocol sets.
729   HTTP_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_0_9);
730   HTTP_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_1_0);
731   HTTP_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_1_1);
732   HTTP2_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_2_0);
733 
734   DEFAULT_TLS_SESSION_PROTOCOL_SET.markAllIn();
735   DEFAULT_TLS_SESSION_PROTOCOL_SET.markOut(TS_ALPN_PROTOCOL_INDEX_HTTP_3);
736   DEFAULT_TLS_SESSION_PROTOCOL_SET.markOut(TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC);
737 
738   DEFAULT_QUIC_SESSION_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_3);
739   DEFAULT_QUIC_SESSION_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC);
740   DEFAULT_QUIC_SESSION_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_3_D27);
741   DEFAULT_QUIC_SESSION_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC_D27);
742 
743   DEFAULT_NON_TLS_SESSION_PROTOCOL_SET = HTTP_PROTOCOL_SET;
744 
745   TSProtoTags.insert(TS_PROTO_TAG_HTTP_1_0);
746   TSProtoTags.insert(TS_PROTO_TAG_HTTP_1_1);
747   TSProtoTags.insert(TS_PROTO_TAG_HTTP_2_0);
748   TSProtoTags.insert(TS_PROTO_TAG_HTTP_3);
749   TSProtoTags.insert(TS_PROTO_TAG_HTTP_QUIC);
750   TSProtoTags.insert(TS_PROTO_TAG_HTTP_3_D27);
751   TSProtoTags.insert(TS_PROTO_TAG_HTTP_QUIC_D27);
752   TSProtoTags.insert(TS_PROTO_TAG_TLS_1_3);
753   TSProtoTags.insert(TS_PROTO_TAG_TLS_1_2);
754   TSProtoTags.insert(TS_PROTO_TAG_TLS_1_1);
755   TSProtoTags.insert(TS_PROTO_TAG_TLS_1_0);
756   TSProtoTags.insert(TS_PROTO_TAG_TCP);
757   TSProtoTags.insert(TS_PROTO_TAG_UDP);
758   TSProtoTags.insert(TS_PROTO_TAG_IPV4);
759   TSProtoTags.insert(TS_PROTO_TAG_IPV6);
760 }
761 
762 const char *
RecNormalizeProtoTag(const char * tag)763 RecNormalizeProtoTag(const char *tag)
764 {
765   auto findResult = TSProtoTags.find(tag);
766   return findResult == TSProtoTags.end() ? nullptr : findResult->data();
767 }
768 
769 /**
770    Convert TS_ALPN_PROTOCOL_INDEX_* into OpenSSL ALPN Wire Format
771 
772    https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_alpn_protos.html
773 
774    TODO: support dynamic generation of wire format
775  */
776 std::string_view
convert_openssl_alpn_wire_format(int index)777 SessionProtocolNameRegistry::convert_openssl_alpn_wire_format(int index)
778 {
779   if (index == TS_ALPN_PROTOCOL_INDEX_HTTP_0_9) {
780     return TS_ALPN_PROTO_ID_OPENSSL_HTTP_0_9;
781   } else if (index == TS_ALPN_PROTOCOL_INDEX_HTTP_1_0) {
782     return TS_ALPN_PROTO_ID_OPENSSL_HTTP_1_0;
783   } else if (index == TS_ALPN_PROTOCOL_INDEX_HTTP_1_1) {
784     return TS_ALPN_PROTO_ID_OPENSSL_HTTP_1_1;
785   } else if (index == TS_ALPN_PROTOCOL_INDEX_HTTP_2_0) {
786     return TS_ALPN_PROTO_ID_OPENSSL_HTTP_2;
787   } else if (index == TS_ALPN_PROTOCOL_INDEX_HTTP_3) {
788     return TS_ALPN_PROTO_ID_OPENSSL_HTTP_3;
789   }
790 
791   return {};
792 }
793 
794 int
toIndex(ts::TextView name)795 SessionProtocolNameRegistry::toIndex(ts::TextView name)
796 {
797   int zret = this->indexFor(name);
798   if (INVALID == zret) {
799     if (m_n < MAX) {
800       // Localize the name by copying it in to the arena.
801       auto text = m_arena.alloc(name.size() + 1).rebind<char>();
802       memcpy(text.data(), name.data(), name.size());
803       text.end()[-1] = '\0';
804       m_names[m_n]   = text.view();
805       zret           = m_n++;
806     } else {
807       ink_release_assert(!"Session protocol name registry overflow");
808     }
809   }
810   return zret;
811 }
812 
813 int
toIndexConst(TextView name)814 SessionProtocolNameRegistry::toIndexConst(TextView name)
815 {
816   int zret = this->indexFor(name);
817   if (INVALID == zret) {
818     if (m_n < MAX) {
819       m_names[m_n] = name;
820       zret         = m_n++;
821     } else {
822       ink_release_assert(!"Session protocol name registry overflow");
823     }
824   }
825   return zret;
826 }
827 
828 int
indexFor(TextView name) const829 SessionProtocolNameRegistry::indexFor(TextView name) const
830 {
831   const ts::TextView *end = m_names.begin() + m_n;
832   auto spot               = std::find(m_names.begin(), end, name);
833   if (spot != end) {
834     return static_cast<int>(spot - m_names.begin());
835   }
836   return INVALID;
837 }
838 
839 ts::TextView
nameFor(int idx) const840 SessionProtocolNameRegistry::nameFor(int idx) const
841 {
842   return 0 <= idx && idx < m_n ? m_names[idx] : TextView{};
843 }
844