/src/trafficserver/src/records/RecHttp.cc
Line | Count | Source |
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/RecCore.h" |
25 | | #include "records/RecHttp.h" |
26 | | #include "tscore/Tokenizer.h" |
27 | | #include <cstring> |
28 | | #include <strings.h> |
29 | | #include "tscore/ink_inet.h" |
30 | | #include "tscore/TextBuffer.h" |
31 | | #include "swoc/BufferWriter.h" |
32 | | #include <cstring> |
33 | | #include <string_view> |
34 | | #include <unordered_set> |
35 | | |
36 | | using swoc::TextView; |
37 | | |
38 | | SessionProtocolNameRegistry globalSessionProtocolNameRegistry; |
39 | | |
40 | | /* Protocol session well-known protocol names. |
41 | | These are also used for NPN setup. |
42 | | */ |
43 | | |
44 | | const char *const TS_ALPN_PROTOCOL_HTTP_0_9 = IP_PROTO_TAG_HTTP_0_9.data(); |
45 | | const char *const TS_ALPN_PROTOCOL_HTTP_1_0 = IP_PROTO_TAG_HTTP_1_0.data(); |
46 | | const char *const TS_ALPN_PROTOCOL_HTTP_1_1 = IP_PROTO_TAG_HTTP_1_1.data(); |
47 | | const char *const TS_ALPN_PROTOCOL_HTTP_2_0 = IP_PROTO_TAG_HTTP_2_0.data(); |
48 | | const char *const TS_ALPN_PROTOCOL_HTTP_3 = IP_PROTO_TAG_HTTP_3.data(); |
49 | | const char *const TS_ALPN_PROTOCOL_HTTP_QUIC = IP_PROTO_TAG_HTTP_QUIC.data(); |
50 | | const char *const TS_ALPN_PROTOCOL_HTTP_3_D29 = IP_PROTO_TAG_HTTP_3_D29.data(); |
51 | | const char *const TS_ALPN_PROTOCOL_HTTP_QUIC_D29 = IP_PROTO_TAG_HTTP_QUIC_D29.data(); |
52 | | |
53 | | const char *const TS_ALPN_PROTOCOL_GROUP_HTTP = "http"; |
54 | | const char *const TS_ALPN_PROTOCOL_GROUP_HTTP2 = "http2"; |
55 | | |
56 | | const char *const TS_PROTO_TAG_HTTP_1_0 = TS_ALPN_PROTOCOL_HTTP_1_0; |
57 | | const char *const TS_PROTO_TAG_HTTP_1_1 = TS_ALPN_PROTOCOL_HTTP_1_1; |
58 | | const char *const TS_PROTO_TAG_HTTP_2_0 = TS_ALPN_PROTOCOL_HTTP_2_0; |
59 | | const char *const TS_PROTO_TAG_HTTP_3 = TS_ALPN_PROTOCOL_HTTP_3; |
60 | | const char *const TS_PROTO_TAG_HTTP_QUIC = TS_ALPN_PROTOCOL_HTTP_QUIC; |
61 | | const char *const TS_PROTO_TAG_HTTP_3_D29 = TS_ALPN_PROTOCOL_HTTP_3_D29; |
62 | | const char *const TS_PROTO_TAG_HTTP_QUIC_D29 = TS_ALPN_PROTOCOL_HTTP_QUIC_D29; |
63 | | const char *const TS_PROTO_TAG_TLS_1_3 = IP_PROTO_TAG_TLS_1_3.data(); |
64 | | const char *const TS_PROTO_TAG_TLS_1_2 = IP_PROTO_TAG_TLS_1_2.data(); |
65 | | const char *const TS_PROTO_TAG_TLS_1_1 = IP_PROTO_TAG_TLS_1_1.data(); |
66 | | const char *const TS_PROTO_TAG_TLS_1_0 = IP_PROTO_TAG_TLS_1_0.data(); |
67 | | const char *const TS_PROTO_TAG_TCP = IP_PROTO_TAG_TCP.data(); |
68 | | const char *const TS_PROTO_TAG_UDP = IP_PROTO_TAG_UDP.data(); |
69 | | const char *const TS_PROTO_TAG_IPV4 = IP_PROTO_TAG_IPV4.data(); |
70 | | const char *const TS_PROTO_TAG_IPV6 = IP_PROTO_TAG_IPV6.data(); |
71 | | |
72 | | std::unordered_set<std::string_view> TSProtoTags; |
73 | | |
74 | | // Precomputed indices for ease of use. |
75 | | int TS_ALPN_PROTOCOL_INDEX_HTTP_0_9 = SessionProtocolNameRegistry::INVALID; |
76 | | int TS_ALPN_PROTOCOL_INDEX_HTTP_1_0 = SessionProtocolNameRegistry::INVALID; |
77 | | int TS_ALPN_PROTOCOL_INDEX_HTTP_1_1 = SessionProtocolNameRegistry::INVALID; |
78 | | int TS_ALPN_PROTOCOL_INDEX_HTTP_2_0 = SessionProtocolNameRegistry::INVALID; |
79 | | int TS_ALPN_PROTOCOL_INDEX_HTTP_3 = SessionProtocolNameRegistry::INVALID; |
80 | | int TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC = SessionProtocolNameRegistry::INVALID; |
81 | | int TS_ALPN_PROTOCOL_INDEX_HTTP_3_D29 = SessionProtocolNameRegistry::INVALID; |
82 | | int TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC_D29 = SessionProtocolNameRegistry::INVALID; |
83 | | |
84 | | // Predefined protocol sets for ease of use. |
85 | | SessionProtocolSet HTTP_PROTOCOL_SET; |
86 | | SessionProtocolSet HTTP2_PROTOCOL_SET; |
87 | | SessionProtocolSet DEFAULT_NON_TLS_SESSION_PROTOCOL_SET; |
88 | | SessionProtocolSet DEFAULT_TLS_SESSION_PROTOCOL_SET; |
89 | | SessionProtocolSet DEFAULT_QUIC_SESSION_PROTOCOL_SET; |
90 | | |
91 | | namespace |
92 | | { |
93 | | |
94 | | DbgCtl dbg_ctl_config{"config"}; |
95 | | DbgCtl dbg_ctl_ssl_alpn{"ssl_alpn"}; |
96 | | |
97 | | } // end anonymous namespace |
98 | | |
99 | | bool |
100 | | mptcp_supported() |
101 | 0 | { |
102 | 0 | int value = 0; |
103 | 0 | #if defined(HAVE_STRUCT_MPTCP_INFO_SUBFLOWS) && defined(MPTCP_INFO) && MPTCP_INFO == 1 |
104 | 0 | ats_scoped_fd fd(::open("/proc/sys/net/mptcp/enabled", O_RDONLY)); |
105 | 0 | if (fd > 0) { |
106 | 0 | TextBuffer buffer(16); |
107 | 0 | buffer.slurp(fd.get()); |
108 | 0 | value = atoi(buffer.bufPtr()); |
109 | 0 | } |
110 | 0 | #endif |
111 | |
|
112 | 0 | return value != 0; |
113 | 0 | } |
114 | | |
115 | | ts::IPAddrPair |
116 | | RecHttpLoadIp(char const *name) |
117 | 0 | { |
118 | 0 | ts::IPAddrPair zret; |
119 | 0 | char value[1024]; |
120 | |
|
121 | 0 | if (RecGetRecordString(name, value, sizeof(value)).has_value()) { |
122 | 0 | Tokenizer tokens(", "); |
123 | 0 | int n_addrs = tokens.Initialize(value); |
124 | 0 | for (int i = 0; i < n_addrs; ++i) { |
125 | 0 | const char *host = tokens[i]; |
126 | | // For backwards compatibility we need to support the use of host names |
127 | | // for the address to bind. |
128 | 0 | auto addrs = ts::getbestaddrinfo(host); |
129 | 0 | if (addrs.has_value()) { |
130 | 0 | if (addrs.has_ip4()) { |
131 | 0 | if (!zret.has_ip4()) { |
132 | 0 | zret = addrs.ip4(); |
133 | 0 | } else { |
134 | 0 | Warning("'%s' specifies more than one IPv4 address, ignoring %s.", name, host); |
135 | 0 | } |
136 | 0 | } |
137 | 0 | if (addrs.has_ip6()) { |
138 | 0 | if (!zret.has_ip6()) { |
139 | 0 | zret = addrs.ip6(); |
140 | 0 | } else { |
141 | 0 | Warning("'%s' specifies more than one IPv6 address, ignoring %s.", name, host); |
142 | 0 | } |
143 | 0 | } |
144 | 0 | } else { |
145 | 0 | Warning("'%s' has an value '%s' that is not recognized as an IP address, ignored.", name, host); |
146 | 0 | } |
147 | 0 | } |
148 | 0 | } |
149 | 0 | return zret; |
150 | 0 | } |
151 | | |
152 | | void |
153 | | RecHttpLoadIpAddrsFromConfVar(const char *value_name, swoc::IPRangeSet &addrs) |
154 | 0 | { |
155 | 0 | char value[1024]; |
156 | |
|
157 | 0 | if (auto sv{RecGetRecordString(value_name, value, sizeof(value))}; sv) { |
158 | 0 | Dbg(dbg_ctl_config, "RecHttpLoadIpAddrsFromConfVar: parsing the name [%s] and value [%s]", value_name, value); |
159 | 0 | swoc::TextView text(sv.value()); |
160 | 0 | while (text) { |
161 | 0 | auto token = text.take_prefix_at(','); |
162 | 0 | if (swoc::IPRange r; r.load(token)) { |
163 | 0 | Dbg(dbg_ctl_config, "RecHttpLoadIpAddrsFromConfVar: marking the value [%.*s]", int(token.size()), token.data()); |
164 | 0 | addrs.mark(r); |
165 | 0 | } |
166 | 0 | } |
167 | 0 | } |
168 | 0 | Dbg(dbg_ctl_config, "RecHttpLoadIpMap: parsed %zu IpMap entries", addrs.count()); |
169 | 0 | } |
170 | | |
171 | | const char *const HttpProxyPort::DEFAULT_VALUE = "8080"; |
172 | | |
173 | | const char *const HttpProxyPort::PORTS_CONFIG_NAME = "proxy.config.http.server_ports"; |
174 | | |
175 | | // "_PREFIX" means the option contains additional data. |
176 | | // Each has a corresponding _LEN value that is the length of the option text. |
177 | | // Options without _PREFIX are just flags with no additional data. |
178 | | |
179 | | const char *const HttpProxyPort::OPT_FD_PREFIX = "fd"; |
180 | | const char *const HttpProxyPort::OPT_OUTBOUND_IP_PREFIX = "ip-out"; |
181 | | const char *const HttpProxyPort::OPT_INBOUND_IP_PREFIX = "ip-in"; |
182 | | const char *const HttpProxyPort::OPT_HOST_RES_PREFIX = "ip-resolve"; |
183 | | const char *const HttpProxyPort::OPT_PROTO_PREFIX = "proto"; |
184 | | |
185 | | const char *const HttpProxyPort::OPT_IPV6 = "ipv6"; |
186 | | const char *const HttpProxyPort::OPT_IPV4 = "ipv4"; |
187 | | const char *const HttpProxyPort::OPT_TRANSPARENT_INBOUND = "tr-in"; |
188 | | const char *const HttpProxyPort::OPT_TRANSPARENT_OUTBOUND = "tr-out"; |
189 | | const char *const HttpProxyPort::OPT_TRANSPARENT_FULL = "tr-full"; |
190 | | const char *const HttpProxyPort::OPT_TRANSPARENT_PASSTHROUGH = "tr-pass"; |
191 | | const char *const HttpProxyPort::OPT_ALLOW_PLAIN = "allow-plain"; |
192 | | const char *const HttpProxyPort::OPT_SSL = "ssl"; |
193 | | const char *const HttpProxyPort::OPT_PROXY_PROTO = "pp"; |
194 | | const char *const HttpProxyPort::OPT_PLUGIN = "plugin"; |
195 | | const char *const HttpProxyPort::OPT_BLIND_TUNNEL = "blind"; |
196 | | const char *const HttpProxyPort::OPT_COMPRESSED = "compressed"; |
197 | | const char *const HttpProxyPort::OPT_MPTCP = "mptcp"; |
198 | | const char *const HttpProxyPort::OPT_QUIC = "quic"; |
199 | | const char *const HttpProxyPort::OPT_PROXY_PROTO_CLIENT_SRC_IP = "pp-clnt"; |
200 | | |
201 | | // File local constants. |
202 | | namespace |
203 | | { |
204 | | // Length values for _PREFIX options. |
205 | | size_t const OPT_FD_PREFIX_LEN = strlen(HttpProxyPort::OPT_FD_PREFIX); |
206 | | size_t const OPT_OUTBOUND_IP_PREFIX_LEN = strlen(HttpProxyPort::OPT_OUTBOUND_IP_PREFIX); |
207 | | size_t const OPT_INBOUND_IP_PREFIX_LEN = strlen(HttpProxyPort::OPT_INBOUND_IP_PREFIX); |
208 | | size_t const OPT_HOST_RES_PREFIX_LEN = strlen(HttpProxyPort::OPT_HOST_RES_PREFIX); |
209 | | size_t const OPT_PROTO_PREFIX_LEN = strlen(HttpProxyPort::OPT_PROTO_PREFIX); |
210 | | |
211 | | constexpr std::string_view TS_ALPN_PROTO_ID_OPENSSL_HTTP_0_9("\x8http/0.9"); |
212 | | constexpr std::string_view TS_ALPN_PROTO_ID_OPENSSL_HTTP_1_0("\x8http/1.0"); |
213 | | constexpr std::string_view TS_ALPN_PROTO_ID_OPENSSL_HTTP_1_1("\x8http/1.1"); |
214 | | constexpr std::string_view TS_ALPN_PROTO_ID_OPENSSL_HTTP_2("\x2h2"); |
215 | | constexpr std::string_view TS_ALPN_PROTO_ID_OPENSSL_HTTP_3("\x2h3"); |
216 | | } // namespace |
217 | | |
218 | | namespace |
219 | | { |
220 | | // Solaris work around. On that OS the compiler will not let me use an |
221 | | // instantiated instance of Vec<self> inside the class, even if |
222 | | // static. So we have to declare it elsewhere and then import via |
223 | | // reference. Might be a problem with Vec<> creating a fixed array |
224 | | // rather than allocating on first use (compared to std::vector<>). |
225 | | HttpProxyPort::Group GLOBAL_DATA; |
226 | | } // namespace |
227 | | HttpProxyPort::Group &HttpProxyPort::m_global = GLOBAL_DATA; |
228 | | |
229 | 0 | HttpProxyPort::HttpProxyPort() : m_fd(ts::NO_FD) |
230 | | |
231 | 0 | { |
232 | 0 | m_host_res_preference = host_res_default_preference_order; |
233 | 0 | } |
234 | | |
235 | | bool |
236 | | HttpProxyPort::hasSSL(Group const &ports) |
237 | 0 | { |
238 | 0 | return std::any_of(ports.begin(), ports.end(), [](HttpProxyPort const &port) { return port.isSSL(); }); |
239 | 0 | } |
240 | | |
241 | | bool |
242 | | HttpProxyPort::hasQUIC(Group const &ports) |
243 | 0 | { |
244 | 0 | bool zret = false; |
245 | 0 | for (int i = 0, n = ports.size(); i < n && !zret; ++i) { |
246 | 0 | if (ports[i].isQUIC()) { |
247 | 0 | zret = true; |
248 | 0 | } |
249 | 0 | } |
250 | 0 | return zret; |
251 | 0 | } |
252 | | |
253 | | const HttpProxyPort * |
254 | | HttpProxyPort::findHttp(Group const &ports, uint16_t family) |
255 | 0 | { |
256 | 0 | bool check_family_p = ats_is_ip(family); |
257 | 0 | const self *zret = nullptr; |
258 | 0 | for (int i = 0, n = ports.size(); i < n && !zret; ++i) { |
259 | 0 | const self &p = ports[i]; |
260 | 0 | if (p.m_port && // has a valid port |
261 | 0 | TRANSPORT_DEFAULT == p.m_type && // is normal HTTP |
262 | 0 | (!check_family_p || p.m_family == family) // right address family |
263 | 0 | ) { |
264 | 0 | zret = &p; |
265 | 0 | }; |
266 | 0 | } |
267 | 0 | return zret; |
268 | 0 | } |
269 | | |
270 | | const char * |
271 | | HttpProxyPort::checkPrefix(const char *src, char const *prefix, size_t prefix_len) |
272 | 0 | { |
273 | 0 | const char *zret = nullptr; |
274 | 0 | if (0 == strncasecmp(prefix, src, prefix_len)) { |
275 | 0 | src += prefix_len; |
276 | 0 | if ('-' == *src || '=' == *src) { |
277 | 0 | ++src; // permit optional '-' or '=' |
278 | 0 | } |
279 | 0 | zret = src; |
280 | 0 | } |
281 | 0 | return zret; |
282 | 0 | } |
283 | | |
284 | | bool |
285 | | HttpProxyPort::loadConfig(std::vector<self> &entries) |
286 | 0 | { |
287 | 0 | auto text{RecGetRecordStringAlloc(PORTS_CONFIG_NAME)}; |
288 | 0 | if (text) { |
289 | 0 | self::loadValue(entries, ats_as_c_str(text)); |
290 | 0 | } |
291 | |
|
292 | 0 | return 0 < entries.size(); |
293 | 0 | } |
294 | | |
295 | | bool |
296 | | HttpProxyPort::loadDefaultIfEmpty(Group &ports) |
297 | 0 | { |
298 | 0 | if (0 == ports.size()) { |
299 | 0 | self::loadValue(ports, DEFAULT_VALUE); |
300 | 0 | } |
301 | |
|
302 | 0 | return 0 < ports.size(); |
303 | 0 | } |
304 | | |
305 | | bool |
306 | | HttpProxyPort::loadValue(std::vector<self> &ports, const char *text) |
307 | 0 | { |
308 | 0 | unsigned old_port_length = ports.size(); // remember this. |
309 | 0 | if (text && *text) { |
310 | 0 | Tokenizer tokens(", "); |
311 | 0 | int n_ports = tokens.Initialize(text); |
312 | 0 | if (n_ports > 0) { |
313 | 0 | for (int p = 0; p < n_ports; ++p) { |
314 | 0 | const char *elt = tokens[p]; |
315 | 0 | HttpProxyPort entry; |
316 | 0 | if (entry.processOptions(elt)) { |
317 | 0 | ports.push_back(entry); |
318 | 0 | } else { |
319 | 0 | Warning("No valid definition was found in proxy port configuration element '%s'", elt); |
320 | 0 | } |
321 | 0 | } |
322 | 0 | } |
323 | 0 | } |
324 | 0 | return ports.size() > old_port_length; // we added at least one port. |
325 | 0 | } |
326 | | |
327 | | bool |
328 | | HttpProxyPort::processOptions(const char *opts) |
329 | 0 | { |
330 | 0 | bool zret = false; // found a port? |
331 | 0 | bool af_set_p = false; // AF explicitly specified? |
332 | 0 | bool host_res_set_p = false; // Host resolution order set explicitly? |
333 | 0 | bool sp_set_p = false; // Session protocol set explicitly? |
334 | 0 | bool bracket_p = false; // found an open bracket in the input? |
335 | 0 | const char *value; // Temp holder for value of a prefix option. |
336 | 0 | IpAddr ip; // temp for loading IP addresses. |
337 | 0 | std::vector<char *> values; // Pointers to single option values. |
338 | | |
339 | | // Make a copy we can modify safely. |
340 | 0 | size_t opts_len = strlen(opts) + 1; |
341 | 0 | char *text = static_cast<char *>(alloca(opts_len)); |
342 | 0 | memcpy(text, opts, opts_len); |
343 | | |
344 | | // Split the copy in to tokens. |
345 | 0 | char *token = nullptr; |
346 | 0 | for (char *spot = text; *spot; ++spot) { |
347 | 0 | if (bracket_p) { |
348 | 0 | if (']' == *spot) { |
349 | 0 | bracket_p = false; |
350 | 0 | } |
351 | 0 | } else if (':' == *spot) { |
352 | 0 | *spot = 0; |
353 | 0 | token = nullptr; |
354 | 0 | } else { |
355 | 0 | if (!token) { |
356 | 0 | token = spot; |
357 | 0 | values.push_back(token); |
358 | 0 | } |
359 | 0 | if ('[' == *spot) { |
360 | 0 | bracket_p = true; |
361 | 0 | } |
362 | 0 | } |
363 | 0 | } |
364 | 0 | if (bracket_p) { |
365 | 0 | Warning("Invalid port descriptor '%s' - left bracket without closing right bracket", opts); |
366 | 0 | return zret; |
367 | 0 | } |
368 | | |
369 | 0 | for (auto item : values) { |
370 | 0 | if (item[0] == '/') { |
371 | 0 | m_family = AF_UNIX; |
372 | 0 | m_unix_path = UnAddr(item); |
373 | 0 | af_set_p = true; |
374 | 0 | zret = true; |
375 | 0 | } else if (isdigit(item[0])) { // leading digit -> port value |
376 | 0 | char *ptr; |
377 | 0 | int port = strtoul(item, &ptr, 10); |
378 | 0 | if (ptr == item) { |
379 | | // really, this shouldn't happen, since we checked for a leading digit. |
380 | 0 | Warning("Mangled port value '%s' in port configuration '%s'", item, opts); |
381 | 0 | } else if (port <= 0 || 65536 <= port) { |
382 | 0 | Warning("Port value '%s' out of range (1..65535) in port configuration '%s'", item, opts); |
383 | 0 | } else { |
384 | 0 | m_port = port; |
385 | 0 | zret = true; |
386 | 0 | } |
387 | 0 | } else if (nullptr != (value = this->checkPrefix(item, OPT_FD_PREFIX, OPT_FD_PREFIX_LEN))) { |
388 | 0 | char *ptr; // tmp for syntax check. |
389 | 0 | int fd = strtoul(value, &ptr, 10); |
390 | 0 | if (ptr == value) { |
391 | 0 | Warning("Mangled file descriptor value '%s' in port descriptor '%s'", item, opts); |
392 | 0 | } else { |
393 | 0 | m_fd = fd; |
394 | 0 | zret = true; |
395 | 0 | } |
396 | 0 | } else if (nullptr != (value = this->checkPrefix(item, OPT_INBOUND_IP_PREFIX, OPT_INBOUND_IP_PREFIX_LEN))) { |
397 | 0 | if (0 == ip.load(value)) { |
398 | 0 | m_inbound_ip = ip; |
399 | 0 | } else { |
400 | 0 | Warning("Invalid IP address value '%s' in port descriptor '%s'", item, opts); |
401 | 0 | } |
402 | 0 | } else if (nullptr != (value = this->checkPrefix(item, OPT_OUTBOUND_IP_PREFIX, OPT_OUTBOUND_IP_PREFIX_LEN))) { |
403 | 0 | if (swoc::IPAddr addr; addr.load(value)) { |
404 | 0 | this->m_outbound = addr; |
405 | 0 | } else { |
406 | 0 | Warning("Invalid IP address value '%s' in port descriptor '%s'", item, opts); |
407 | 0 | } |
408 | 0 | } else if (0 == strcasecmp(OPT_COMPRESSED, item)) { |
409 | 0 | m_type = TRANSPORT_COMPRESSED; |
410 | 0 | } else if (0 == strcasecmp(OPT_BLIND_TUNNEL, item)) { |
411 | 0 | m_type = TRANSPORT_BLIND_TUNNEL; |
412 | 0 | } else if (0 == strcasecmp(OPT_IPV6, item)) { |
413 | 0 | if (m_family != AF_UNIX) { |
414 | 0 | m_family = AF_INET6; |
415 | 0 | af_set_p = true; |
416 | 0 | } else { |
417 | 0 | Warning("Invalid ipv6 specification after unix domain path specified"); |
418 | 0 | } |
419 | 0 | } else if (0 == strcasecmp(OPT_IPV4, item)) { |
420 | 0 | if (m_family != AF_UNIX) { |
421 | 0 | m_family = AF_INET; |
422 | 0 | af_set_p = true; |
423 | 0 | } else { |
424 | 0 | Warning("Invalid ipv4 specification after unix domain path specified"); |
425 | 0 | } |
426 | 0 | } else if (0 == strcasecmp(OPT_SSL, item)) { |
427 | 0 | m_type = TRANSPORT_SSL; |
428 | 0 | #if TS_USE_QUIC == 1 |
429 | 0 | } else if (0 == strcasecmp(OPT_QUIC, item)) { |
430 | 0 | m_type = TRANSPORT_QUIC; |
431 | 0 | #endif |
432 | 0 | } else if (0 == strcasecmp(OPT_PLUGIN, item)) { |
433 | 0 | m_type = TRANSPORT_PLUGIN; |
434 | 0 | } else if (0 == strcasecmp(OPT_PROXY_PROTO, item)) { |
435 | 0 | m_proxy_protocol = true; |
436 | 0 | } else if (0 == strcasecmp(OPT_TRANSPARENT_INBOUND, item)) { |
437 | | #if TS_USE_TPROXY |
438 | | m_inbound_transparent_p = true; |
439 | | #else |
440 | 0 | Warning("Transparency requested [%s] in port descriptor '%s' but TPROXY was not configured.", item, opts); |
441 | 0 | #endif |
442 | 0 | } else if (0 == strcasecmp(OPT_TRANSPARENT_OUTBOUND, item)) { |
443 | | #if TS_USE_TPROXY |
444 | | m_outbound_transparent_p = true; |
445 | | #else |
446 | 0 | Warning("Transparency requested [%s] in port descriptor '%s' but TPROXY was not configured.", item, opts); |
447 | 0 | #endif |
448 | 0 | } else if (0 == strcasecmp(OPT_TRANSPARENT_FULL, item)) { |
449 | | #if TS_USE_TPROXY |
450 | | m_inbound_transparent_p = true; |
451 | | m_outbound_transparent_p = true; |
452 | | #else |
453 | 0 | Warning("Transparency requested [%s] in port descriptor '%s' but TPROXY was not configured.", item, opts); |
454 | 0 | #endif |
455 | 0 | } else if (0 == strcasecmp(OPT_TRANSPARENT_PASSTHROUGH, item)) { |
456 | | #if TS_USE_TPROXY |
457 | | m_transparent_passthrough = true; |
458 | | #else |
459 | 0 | Warning("Transparent pass-through requested [%s] in port descriptor '%s' but TPROXY was not configured.", item, opts); |
460 | 0 | #endif |
461 | 0 | } else if (0 == strcasecmp(OPT_ALLOW_PLAIN, item)) { |
462 | 0 | m_allow_plain = true; |
463 | 0 | } else if (0 == strcasecmp(OPT_MPTCP, item)) { |
464 | 0 | if (mptcp_supported()) { |
465 | 0 | m_mptcp = true; |
466 | 0 | } else { |
467 | 0 | Warning("Multipath TCP requested [%s] in port descriptor '%s' but it is not supported by this host.", item, opts); |
468 | 0 | } |
469 | 0 | } else if (0 == strcasecmp(OPT_PROXY_PROTO_CLIENT_SRC_IP, item)) { |
470 | 0 | m_proxy_protocol_client_src = true; |
471 | 0 | } else if (nullptr != (value = this->checkPrefix(item, OPT_HOST_RES_PREFIX, OPT_HOST_RES_PREFIX_LEN))) { |
472 | 0 | this->processFamilyPreference(value); |
473 | 0 | host_res_set_p = true; |
474 | 0 | } else if (nullptr != (value = this->checkPrefix(item, OPT_PROTO_PREFIX, OPT_PROTO_PREFIX_LEN))) { |
475 | 0 | this->processSessionProtocolPreference(value); |
476 | 0 | sp_set_p = true; |
477 | 0 | } else { |
478 | 0 | Warning("Invalid option '%s' in proxy port descriptor '%s'", item, opts); |
479 | 0 | } |
480 | 0 | } |
481 | |
|
482 | 0 | bool in_ip_set_p = m_inbound_ip.isValid(); |
483 | |
|
484 | 0 | if (af_set_p) { |
485 | 0 | if (in_ip_set_p && m_family != m_inbound_ip.family()) { |
486 | 0 | std::string_view iname{ats_ip_family_name(m_inbound_ip.family())}; |
487 | 0 | std::string_view fname{ats_ip_family_name(m_family)}; |
488 | 0 | Warning("Invalid port descriptor '%s' - the inbound address family [%.*s] is not the same type as the explicit family value " |
489 | 0 | "[%.*s]", |
490 | 0 | opts, static_cast<int>(iname.size()), iname.data(), static_cast<int>(fname.size()), fname.data()); |
491 | 0 | zret = false; |
492 | 0 | } |
493 | 0 | } else if (in_ip_set_p) { |
494 | 0 | m_family = m_inbound_ip.family(); // set according to address. |
495 | 0 | } |
496 | | |
497 | | // If the port is outbound transparent only CLIENT host resolution is possible. |
498 | 0 | if (m_outbound_transparent_p) { |
499 | 0 | if (host_res_set_p && |
500 | 0 | (m_host_res_preference[0] != HOST_RES_PREFER_CLIENT || m_host_res_preference[1] != HOST_RES_PREFER_NONE)) { |
501 | 0 | Warning("Outbound transparent port '%s' requires the IP address resolution ordering '%s,%s'. " |
502 | 0 | "This is set automatically and does not need to be set explicitly.", |
503 | 0 | opts, HOST_RES_PREFERENCE_STRING[HOST_RES_PREFER_CLIENT], HOST_RES_PREFERENCE_STRING[HOST_RES_PREFER_NONE]); |
504 | 0 | } |
505 | 0 | m_host_res_preference[0] = HOST_RES_PREFER_CLIENT; |
506 | 0 | m_host_res_preference[1] = HOST_RES_PREFER_NONE; |
507 | 0 | } |
508 | | |
509 | | // Transparent pass-through requires tr-in |
510 | 0 | if (m_transparent_passthrough && !m_inbound_transparent_p) { |
511 | 0 | Warning("Port descriptor '%s' has transparent pass-through enabled without inbound transparency, this will be ignored.", opts); |
512 | 0 | m_transparent_passthrough = false; |
513 | 0 | } |
514 | | |
515 | | // Make sure QUIC is not enabled with incompatible options |
516 | 0 | if (this->isQUIC()) { |
517 | 0 | if (this->m_allow_plain) { |
518 | 0 | Warning("allow_plain incompatible with QUIC"); |
519 | 0 | zret = false; |
520 | 0 | } else if (this->m_inbound_transparent_p || this->m_outbound_transparent_p) { |
521 | 0 | Warning("transparent mode not supported with QUIC"); |
522 | 0 | zret = false; |
523 | 0 | } |
524 | 0 | } |
525 | | |
526 | | // Set the default session protocols. |
527 | 0 | if (!sp_set_p) { |
528 | 0 | if (this->isSSL()) { |
529 | 0 | m_session_protocol_preference = DEFAULT_TLS_SESSION_PROTOCOL_SET; |
530 | 0 | } else if (this->isQUIC()) { |
531 | 0 | m_session_protocol_preference = DEFAULT_QUIC_SESSION_PROTOCOL_SET; |
532 | 0 | } else { |
533 | 0 | m_session_protocol_preference = DEFAULT_NON_TLS_SESSION_PROTOCOL_SET; |
534 | 0 | } |
535 | 0 | } |
536 | |
|
537 | 0 | return zret; |
538 | 0 | } |
539 | | |
540 | | void |
541 | | HttpProxyPort::processFamilyPreference(const char *value) |
542 | 0 | { |
543 | 0 | parse_host_res_preference(value, m_host_res_preference); |
544 | 0 | } |
545 | | |
546 | | void |
547 | | HttpProxyPort::processSessionProtocolPreference(const char *value) |
548 | 0 | { |
549 | 0 | m_session_protocol_preference.markAllOut(); |
550 | 0 | globalSessionProtocolNameRegistry.markIn(value, m_session_protocol_preference); |
551 | 0 | } |
552 | | |
553 | | void |
554 | | SessionProtocolNameRegistry::markIn(const char *value, SessionProtocolSet &sp_set) |
555 | 0 | { |
556 | 0 | int n; // # of tokens |
557 | 0 | Tokenizer tokens(" ;|,:"); |
558 | |
|
559 | 0 | n = tokens.Initialize(value); |
560 | |
|
561 | 0 | for (int i = 0; i < n; ++i) { |
562 | 0 | const char *elt = tokens[i]; |
563 | | |
564 | | /// Check special cases |
565 | 0 | if (0 == strcasecmp(elt, TS_ALPN_PROTOCOL_GROUP_HTTP)) { |
566 | 0 | sp_set.markIn(HTTP_PROTOCOL_SET); |
567 | 0 | } else if (0 == strcasecmp(elt, TS_ALPN_PROTOCOL_GROUP_HTTP2)) { |
568 | 0 | sp_set.markIn(HTTP2_PROTOCOL_SET); |
569 | 0 | } else { // user defined - register and mark. |
570 | 0 | int idx = globalSessionProtocolNameRegistry.toIndex(TextView{elt, strlen(elt)}); |
571 | 0 | sp_set.markIn(idx); |
572 | 0 | } |
573 | 0 | } |
574 | 0 | } |
575 | | |
576 | | int |
577 | | HttpProxyPort::print(char *out, size_t n) |
578 | 0 | { |
579 | 0 | size_t zret = 0; // # of chars printed so far. |
580 | 0 | ip_text_buffer ipb; |
581 | 0 | bool need_colon_p = false; |
582 | |
|
583 | 0 | if (m_inbound_ip.isValid()) { |
584 | 0 | zret += snprintf(out + zret, n - zret, "%s=[%s]", OPT_INBOUND_IP_PREFIX, m_inbound_ip.toString(ipb, sizeof(ipb))); |
585 | 0 | need_colon_p = true; |
586 | 0 | } |
587 | 0 | if (zret >= n) { |
588 | 0 | return n; |
589 | 0 | } |
590 | | |
591 | 0 | if (m_outbound.has_ip4()) { |
592 | 0 | if (need_colon_p) { |
593 | 0 | out[zret++] = ':'; |
594 | 0 | } |
595 | 0 | zret += snprintf(out + zret, n - zret, "%s=[%s]", OPT_OUTBOUND_IP_PREFIX, |
596 | 0 | swoc::FixedBufferWriter(ipb, sizeof(ipb)).print("{}", m_outbound.ip4()).data()); |
597 | 0 | need_colon_p = true; |
598 | 0 | } |
599 | 0 | if (zret >= n) { |
600 | 0 | return n; |
601 | 0 | } |
602 | | |
603 | 0 | if (m_outbound.has_ip6()) { |
604 | 0 | if (need_colon_p) { |
605 | 0 | out[zret++] = ':'; |
606 | 0 | } |
607 | 0 | zret += snprintf(out + zret, n - zret, "%s=[%s]", OPT_OUTBOUND_IP_PREFIX, |
608 | 0 | swoc::FixedBufferWriter(ipb, sizeof(ipb)).print("{}", m_outbound.ip6()).data()); |
609 | 0 | need_colon_p = true; |
610 | 0 | } |
611 | 0 | if (zret >= n) { |
612 | 0 | return n; |
613 | 0 | } |
614 | | |
615 | 0 | if (0 != m_port) { |
616 | 0 | if (need_colon_p) { |
617 | 0 | out[zret++] = ':'; |
618 | 0 | } |
619 | 0 | zret += snprintf(out + zret, n - zret, "%d", m_port); |
620 | 0 | need_colon_p = true; |
621 | 0 | } |
622 | 0 | if (zret >= n) { |
623 | 0 | return n; |
624 | 0 | } |
625 | | |
626 | 0 | if (ts::NO_FD != m_fd) { |
627 | 0 | if (need_colon_p) { |
628 | 0 | out[zret++] = ':'; |
629 | 0 | } |
630 | 0 | zret += snprintf(out + zret, n - zret, "fd=%d", m_fd); |
631 | 0 | } |
632 | 0 | if (zret >= n) { |
633 | 0 | return n; |
634 | 0 | } |
635 | | |
636 | | // After this point, all of these options require other options which we've already |
637 | | // generated so all of them need a leading colon and we can stop checking for that. |
638 | | |
639 | 0 | if (AF_INET6 == m_family) { |
640 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_IPV6); |
641 | 0 | } |
642 | 0 | if (zret >= n) { |
643 | 0 | return n; |
644 | 0 | } |
645 | | |
646 | 0 | if (TRANSPORT_BLIND_TUNNEL == m_type) { |
647 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_BLIND_TUNNEL); |
648 | 0 | } else if (TRANSPORT_SSL == m_type) { |
649 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_SSL); |
650 | 0 | } else if (TRANSPORT_QUIC == m_type) { |
651 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_QUIC); |
652 | 0 | } else if (TRANSPORT_PLUGIN == m_type) { |
653 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_PLUGIN); |
654 | 0 | } else if (TRANSPORT_COMPRESSED == m_type) { |
655 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_COMPRESSED); |
656 | 0 | } |
657 | 0 | if (zret >= n) { |
658 | 0 | return n; |
659 | 0 | } |
660 | | |
661 | 0 | if (m_proxy_protocol) { |
662 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_PROXY_PROTO); |
663 | 0 | } |
664 | |
|
665 | 0 | if (m_proxy_protocol_client_src) { |
666 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_PROXY_PROTO_CLIENT_SRC_IP); |
667 | 0 | } |
668 | |
|
669 | 0 | if (m_outbound_transparent_p && m_inbound_transparent_p) { |
670 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_TRANSPARENT_FULL); |
671 | 0 | } else if (m_inbound_transparent_p) { |
672 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_TRANSPARENT_INBOUND); |
673 | 0 | } else if (m_outbound_transparent_p) { |
674 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_TRANSPARENT_OUTBOUND); |
675 | 0 | } |
676 | |
|
677 | 0 | if (m_mptcp) { |
678 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_MPTCP); |
679 | 0 | } |
680 | |
|
681 | 0 | if (m_transparent_passthrough) { |
682 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_TRANSPARENT_PASSTHROUGH); |
683 | 0 | } |
684 | |
|
685 | 0 | if (m_allow_plain) { |
686 | 0 | zret += snprintf(out + zret, n - zret, ":%s", OPT_ALLOW_PLAIN); |
687 | 0 | } |
688 | | |
689 | | /* Don't print the IP resolution preferences if the port is outbound |
690 | | * transparent (which means the preference order is forced) or if |
691 | | * the order is the same as the default. |
692 | | */ |
693 | 0 | if (!m_outbound_transparent_p && m_host_res_preference != host_res_default_preference_order) { |
694 | 0 | zret += snprintf(out + zret, n - zret, ":%s=", OPT_HOST_RES_PREFIX); |
695 | 0 | zret += ts_host_res_order_to_string(m_host_res_preference, out + zret, n - zret); |
696 | 0 | } |
697 | | |
698 | | // session protocol options - look for condensed options first |
699 | | // first two cases are the defaults so if those match, print nothing. |
700 | 0 | SessionProtocolSet sp_set = m_session_protocol_preference; // need to modify so copy. |
701 | 0 | need_colon_p = true; // for listing case, turned off if we do a special case. |
702 | 0 | if (sp_set == DEFAULT_NON_TLS_SESSION_PROTOCOL_SET && !this->isSSL()) { |
703 | 0 | sp_set.markOut(DEFAULT_NON_TLS_SESSION_PROTOCOL_SET); |
704 | 0 | } else if (sp_set == DEFAULT_TLS_SESSION_PROTOCOL_SET && this->isSSL()) { |
705 | 0 | sp_set.markOut(DEFAULT_TLS_SESSION_PROTOCOL_SET); |
706 | 0 | } else if (sp_set == DEFAULT_QUIC_SESSION_PROTOCOL_SET && this->isQUIC()) { |
707 | 0 | sp_set.markOut(DEFAULT_QUIC_SESSION_PROTOCOL_SET); |
708 | 0 | } |
709 | | |
710 | | // pull out groups. |
711 | 0 | if (sp_set.contains(HTTP_PROTOCOL_SET)) { |
712 | 0 | zret += snprintf(out + zret, n - zret, ":%s=%s", OPT_PROTO_PREFIX, TS_ALPN_PROTOCOL_GROUP_HTTP); |
713 | 0 | sp_set.markOut(HTTP_PROTOCOL_SET); |
714 | 0 | need_colon_p = false; |
715 | 0 | } |
716 | 0 | if (sp_set.contains(HTTP2_PROTOCOL_SET)) { |
717 | 0 | if (need_colon_p) { |
718 | 0 | zret += snprintf(out + zret, n - zret, ":%s=", OPT_PROTO_PREFIX); |
719 | 0 | } else { |
720 | 0 | out[zret++] = ';'; |
721 | 0 | } |
722 | 0 | zret += snprintf(out + zret, n - zret, "%s", TS_ALPN_PROTOCOL_GROUP_HTTP2); |
723 | 0 | sp_set.markOut(HTTP2_PROTOCOL_SET); |
724 | 0 | need_colon_p = false; |
725 | 0 | } |
726 | | // now enumerate what's left. |
727 | 0 | if (!sp_set.isEmpty()) { |
728 | 0 | if (need_colon_p) { |
729 | 0 | zret += snprintf(out + zret, n - zret, ":%s=", OPT_PROTO_PREFIX); |
730 | 0 | } |
731 | 0 | bool sep_p = !need_colon_p; |
732 | 0 | for (int k = 0; k < SessionProtocolSet::MAX; ++k) { |
733 | 0 | if (sp_set.contains(k)) { |
734 | 0 | auto name{globalSessionProtocolNameRegistry.nameFor(k)}; |
735 | 0 | zret += snprintf(out + zret, n - zret, "%s%.*s", sep_p ? ";" : "", static_cast<int>(name.size()), name.data()); |
736 | 0 | sep_p = true; |
737 | 0 | } |
738 | 0 | } |
739 | 0 | } |
740 | |
|
741 | 0 | return std::min(zret, n); |
742 | 0 | } |
743 | | |
744 | | void |
745 | | ts_host_res_global_init() |
746 | 0 | { |
747 | | // Global configuration values. |
748 | 0 | host_res_default_preference_order = HOST_RES_DEFAULT_PREFERENCE_ORDER; |
749 | 0 | auto str{RecGetRecordStringAlloc("proxy.config.hostdb.ip_resolve")}; |
750 | 0 | auto ip_resolve{ats_as_c_str(str)}; |
751 | 0 | if (ip_resolve) { |
752 | 0 | parse_host_res_preference(ip_resolve, host_res_default_preference_order); |
753 | 0 | } |
754 | 0 | } |
755 | | |
756 | | // Whatever executable uses librecords must call this. |
757 | | void |
758 | | ts_session_protocol_well_known_name_indices_init() |
759 | 231 | { |
760 | | // register all the well known protocols and get the indices set. |
761 | 231 | TS_ALPN_PROTOCOL_INDEX_HTTP_0_9 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_0_9}); |
762 | 231 | TS_ALPN_PROTOCOL_INDEX_HTTP_1_0 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_1_0}); |
763 | 231 | TS_ALPN_PROTOCOL_INDEX_HTTP_1_1 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_1_1}); |
764 | 231 | TS_ALPN_PROTOCOL_INDEX_HTTP_2_0 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_2_0}); |
765 | 231 | TS_ALPN_PROTOCOL_INDEX_HTTP_3 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_3}); |
766 | 231 | TS_ALPN_PROTOCOL_INDEX_HTTP_3_D29 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_3_D29}); |
767 | 231 | TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_QUIC}); |
768 | 231 | TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC_D29 = |
769 | 231 | globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_QUIC_D29}); |
770 | | |
771 | | // Now do the predefined protocol sets. |
772 | 231 | HTTP_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_0_9); |
773 | 231 | HTTP_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_1_0); |
774 | 231 | HTTP_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_1_1); |
775 | 231 | HTTP2_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_2_0); |
776 | | |
777 | 231 | DEFAULT_TLS_SESSION_PROTOCOL_SET.markAllIn(); |
778 | 231 | DEFAULT_TLS_SESSION_PROTOCOL_SET.markOut(TS_ALPN_PROTOCOL_INDEX_HTTP_3); |
779 | 231 | DEFAULT_TLS_SESSION_PROTOCOL_SET.markOut(TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC); |
780 | | |
781 | 231 | DEFAULT_QUIC_SESSION_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_3); |
782 | 231 | DEFAULT_QUIC_SESSION_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC); |
783 | 231 | DEFAULT_QUIC_SESSION_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_3_D29); |
784 | 231 | DEFAULT_QUIC_SESSION_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC_D29); |
785 | | |
786 | 231 | DEFAULT_NON_TLS_SESSION_PROTOCOL_SET = HTTP_PROTOCOL_SET; |
787 | | |
788 | 231 | TSProtoTags.insert(TS_PROTO_TAG_HTTP_1_0); |
789 | 231 | TSProtoTags.insert(TS_PROTO_TAG_HTTP_1_1); |
790 | 231 | TSProtoTags.insert(TS_PROTO_TAG_HTTP_2_0); |
791 | 231 | TSProtoTags.insert(TS_PROTO_TAG_HTTP_3); |
792 | 231 | TSProtoTags.insert(TS_PROTO_TAG_HTTP_QUIC); |
793 | 231 | TSProtoTags.insert(TS_PROTO_TAG_HTTP_3_D29); |
794 | 231 | TSProtoTags.insert(TS_PROTO_TAG_HTTP_QUIC_D29); |
795 | 231 | TSProtoTags.insert(TS_PROTO_TAG_TLS_1_3); |
796 | 231 | TSProtoTags.insert(TS_PROTO_TAG_TLS_1_2); |
797 | 231 | TSProtoTags.insert(TS_PROTO_TAG_TLS_1_1); |
798 | 231 | TSProtoTags.insert(TS_PROTO_TAG_TLS_1_0); |
799 | 231 | TSProtoTags.insert(TS_PROTO_TAG_TCP); |
800 | 231 | TSProtoTags.insert(TS_PROTO_TAG_UDP); |
801 | 231 | TSProtoTags.insert(TS_PROTO_TAG_IPV4); |
802 | 231 | TSProtoTags.insert(TS_PROTO_TAG_IPV6); |
803 | 231 | } |
804 | | |
805 | | const char * |
806 | | RecNormalizeProtoTag(const char *tag) |
807 | 0 | { |
808 | 0 | auto findResult = TSProtoTags.find(tag); |
809 | 0 | return findResult == TSProtoTags.end() ? nullptr : findResult->data(); |
810 | 0 | } |
811 | | |
812 | | /** |
813 | | Convert TS_ALPN_PROTOCOL_INDEX_* into OpenSSL ALPN Wire Format |
814 | | |
815 | | https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_alpn_protos.html |
816 | | |
817 | | TODO: support dynamic generation of wire format |
818 | | */ |
819 | | std::string_view |
820 | | SessionProtocolNameRegistry::convert_openssl_alpn_wire_format(int index) |
821 | 220 | { |
822 | 220 | if (index == TS_ALPN_PROTOCOL_INDEX_HTTP_0_9) { |
823 | 0 | return TS_ALPN_PROTO_ID_OPENSSL_HTTP_0_9; |
824 | 220 | } else if (index == TS_ALPN_PROTOCOL_INDEX_HTTP_1_0) { |
825 | 24 | return TS_ALPN_PROTO_ID_OPENSSL_HTTP_1_0; |
826 | 196 | } else if (index == TS_ALPN_PROTOCOL_INDEX_HTTP_1_1) { |
827 | 18 | return TS_ALPN_PROTO_ID_OPENSSL_HTTP_1_1; |
828 | 178 | } else if (index == TS_ALPN_PROTOCOL_INDEX_HTTP_2_0) { |
829 | 178 | return TS_ALPN_PROTO_ID_OPENSSL_HTTP_2; |
830 | 178 | } else if (index == TS_ALPN_PROTOCOL_INDEX_HTTP_3) { |
831 | 0 | return TS_ALPN_PROTO_ID_OPENSSL_HTTP_3; |
832 | 0 | } |
833 | | |
834 | 0 | return {}; |
835 | 220 | } |
836 | | |
837 | | int |
838 | | SessionProtocolNameRegistry::toIndex(swoc::TextView name) |
839 | 0 | { |
840 | 0 | int zret = this->indexFor(name); |
841 | 0 | if (INVALID == zret) { |
842 | 0 | if (m_n < MAX) { |
843 | | // Localize the name by copying it in to the arena. |
844 | 0 | auto text = m_arena.alloc(name.size() + 1).rebind<char>(); |
845 | 0 | memcpy(text, name); |
846 | 0 | text.end()[-1] = '\0'; |
847 | 0 | m_names[m_n].assign(text.data(), name.size()); |
848 | 0 | zret = m_n++; |
849 | 0 | } else { |
850 | 0 | ink_release_assert(!"Session protocol name registry overflow"); |
851 | 0 | } |
852 | 0 | } |
853 | 0 | return zret; |
854 | 0 | } |
855 | | |
856 | | int |
857 | | SessionProtocolNameRegistry::toIndexConst(TextView name) |
858 | 1.84k | { |
859 | 1.84k | int zret = this->indexFor(name); |
860 | 1.84k | if (INVALID == zret) { |
861 | 8 | if (m_n < MAX) { |
862 | 8 | m_names[m_n] = name; |
863 | 8 | zret = m_n++; |
864 | 8 | } else { |
865 | 0 | ink_release_assert(!"Session protocol name registry overflow"); |
866 | 0 | } |
867 | 8 | } |
868 | 1.84k | return zret; |
869 | 1.84k | } |
870 | | |
871 | | int |
872 | | SessionProtocolNameRegistry::indexFor(TextView name) const |
873 | 2.23k | { |
874 | 2.23k | const swoc::TextView *end = m_names.begin() + m_n; |
875 | 2.23k | auto spot = std::find(m_names.begin(), end, name); |
876 | 2.23k | if (spot != end) { |
877 | 2.06k | return static_cast<int>(spot - m_names.begin()); |
878 | 2.06k | } |
879 | 168 | return INVALID; |
880 | 2.23k | } |
881 | | |
882 | | swoc::TextView |
883 | | SessionProtocolNameRegistry::nameFor(int idx) const |
884 | 0 | { |
885 | 0 | return 0 <= idx && idx < m_n ? m_names[idx] : TextView{}; |
886 | 0 | } |
887 | | |
888 | | bool |
889 | | convert_alpn_to_wire_format(std::string_view protocols_sv, unsigned char *wire_format_buffer, int &wire_format_buffer_len) |
890 | 231 | { |
891 | | // TODO: once the protocols_sv is switched to be a TextView (see the TODO |
892 | | // comment in this functions doxygen comment), then rename the input |
893 | | // parameter to be simply `protocols` and remove this next line. |
894 | 231 | TextView protocols(protocols_sv); |
895 | | // Callers expect wire_format_buffer_len to be zero'd out in the event of an |
896 | | // error. To simplify the error handling from doing this on every return, we |
897 | | // simply zero them out here at the start. |
898 | 231 | auto const orig_wire_format_buffer_len = wire_format_buffer_len; |
899 | 231 | memset(wire_format_buffer, 0, wire_format_buffer_len); |
900 | 231 | wire_format_buffer_len = 0; |
901 | | |
902 | 231 | if (protocols.empty()) { |
903 | 0 | return false; |
904 | 0 | } |
905 | | |
906 | | // Parse the comma separated protocol string into a list of protocol names. |
907 | 231 | std::vector<std::string_view> alpn_protocols; |
908 | 231 | TextView protocol; |
909 | 231 | int computed_alpn_array_len = 0; |
910 | | |
911 | 444 | while (protocols) { |
912 | 426 | protocol = protocols.take_prefix_at(',').trim_if(&isspace); |
913 | 426 | if (protocol.empty()) { |
914 | 17 | Error("Empty protocol name in configured ALPN list: \"%.*s\"", static_cast<int>(protocols.size()), protocols.data()); |
915 | 17 | return false; |
916 | 17 | } |
917 | 409 | if (protocol.size() > 255) { |
918 | | // The length has to fit in one byte. |
919 | 20 | Error("A protocol name larger than 255 bytes in configured ALPN list: \"%.*s\"", static_cast<int>(protocols.size()), |
920 | 20 | protocols.data()); |
921 | 20 | return false; |
922 | 20 | } |
923 | | // Check whether we recognize the protocol. |
924 | 389 | auto const protocol_index = globalSessionProtocolNameRegistry.indexFor(protocol); |
925 | 389 | if (protocol_index == SessionProtocolNameRegistry::INVALID) { |
926 | 160 | Error("Unknown protocol name in configured ALPN list: \"%.*s\"", static_cast<int>(protocol.size()), protocol.data()); |
927 | 160 | return false; |
928 | 160 | } |
929 | | // Make sure the protocol is one of our supported protocols. |
930 | 229 | if (protocol_index == TS_ALPN_PROTOCOL_INDEX_HTTP_0_9 || |
931 | 228 | (!HTTP_PROTOCOL_SET.contains(protocol_index) && !HTTP2_PROTOCOL_SET.contains(protocol_index))) { |
932 | 9 | Error("Unsupported protocol name in configured ALPN list: %.*s", static_cast<int>(protocol.size()), protocol.data()); |
933 | 9 | return false; |
934 | 9 | } |
935 | | |
936 | 220 | auto const protocol_wire_format = globalSessionProtocolNameRegistry.convert_openssl_alpn_wire_format(protocol_index); |
937 | 220 | computed_alpn_array_len += protocol_wire_format.size(); |
938 | 220 | if (computed_alpn_array_len > orig_wire_format_buffer_len) { |
939 | | // We have exceeded the size of the output buffer. |
940 | 7 | Error("The output ALPN length (%d bytes) is larger than the output buffer size of %d bytes", computed_alpn_array_len, |
941 | 7 | orig_wire_format_buffer_len); |
942 | 7 | return false; |
943 | 7 | } |
944 | | |
945 | 213 | alpn_protocols.push_back(protocol_wire_format); |
946 | 213 | } |
947 | 18 | if (alpn_protocols.empty()) { |
948 | 0 | Error("No protocols specified in ALPN list: \"%.*s\"", static_cast<int>(protocols.size()), protocols.data()); |
949 | 0 | return false; |
950 | 0 | } |
951 | | |
952 | | // All checks pass and the protocols are parsed. Write the result to the |
953 | | // output buffer. |
954 | 18 | auto *end = wire_format_buffer; |
955 | 69 | for (auto &protocol : alpn_protocols) { |
956 | 69 | auto const len = protocol.size(); |
957 | 69 | memcpy(end, protocol.data(), len); |
958 | 69 | end += len; |
959 | 69 | } |
960 | 18 | wire_format_buffer_len = computed_alpn_array_len; |
961 | 18 | Dbg(dbg_ctl_ssl_alpn, "Successfully converted ALPN list to wire format: \"%.*s\"", static_cast<int>(protocols.size()), |
962 | 18 | protocols.data()); |
963 | 18 | return true; |
964 | 18 | } |