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