From be543293565fcbe775c233f2a43a097518c358da Mon Sep 17 00:00:00 2001 From: Susan Hinrichs Date: Mon, 7 Jun 2021 09:14:28 -0500 Subject: [PATCH 01/33] Remove bucket search from IntrusiveHashMap::erase (#7848) --- include/tscore/IntrusiveHashMap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/tscore/IntrusiveHashMap.h b/include/tscore/IntrusiveHashMap.h index f8dd340aff2..83bad931b71 100644 --- a/include/tscore/IntrusiveHashMap.h +++ b/include/tscore/IntrusiveHashMap.h @@ -578,9 +578,9 @@ IntrusiveHashMap::erase(iterator const &loc) -> iterator template bool -IntrusiveHashMap::erase(value_type *value) +IntrusiveHashMap::erase(value_type *v) { - auto loc = this->find(value); + auto loc = this->iterator_for(v); if (loc != this->end()) { this->erase(loc); return true; From b62da7d71546f13bee2f6e721428849a95eae3b5 Mon Sep 17 00:00:00 2001 From: Susan Hinrichs Date: Mon, 7 Jun 2021 20:03:42 -0500 Subject: [PATCH 02/33] Adjust vc read errors (#7923) * Adjust vc read errors * Adjust assert assumptions * Try adjusting the read error again * Remove the error signalling on read completely --- iocore/net/UnixNetVConnection.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/iocore/net/UnixNetVConnection.cc b/iocore/net/UnixNetVConnection.cc index 58e2fc09a3d..b253f81b622 100644 --- a/iocore/net/UnixNetVConnection.cc +++ b/iocore/net/UnixNetVConnection.cc @@ -199,12 +199,6 @@ read_from_net(NetHandler *nh, UnixNetVConnection *vc, EThread *thread) return; } - if (vc->has_error()) { - vc->lerrno = vc->error; - vc->readSignalAndUpdate(VC_EVENT_ERROR); - return; - } - // It is possible that the closed flag got set from HttpSessionManager in the // global session pool case. If so, the closed flag should be stable once we get the // s->vio.mutex (the global session pool mutex). @@ -337,6 +331,7 @@ read_from_net(NetHandler *nh, UnixNetVConnection *vc, EThread *thread) } } } + // If here are is no more room, or nothing to do, disable the connection if (s->vio.ntodo() <= 0 || !s->enabled || !buf.writer()->write_avail()) { read_disable(nh, vc); From 026bf04d786695dad6269644deca91f36f5481e3 Mon Sep 17 00:00:00 2001 From: Masaori Koshiba Date: Tue, 8 Jun 2021 10:40:26 +0900 Subject: [PATCH 03/33] AuTest: Enable h2spec generic test cases (#7926) --- tests/gold_tests/h2/h2spec.test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gold_tests/h2/h2spec.test.py b/tests/gold_tests/h2/h2spec.test.py index f6ab102c6e4..37740dd7c96 100644 --- a/tests/gold_tests/h2/h2spec.test.py +++ b/tests/gold_tests/h2/h2spec.test.py @@ -59,7 +59,7 @@ # ---- # In case you need to disable some of the tests, you can specify sections like http2/6.4. -h2spec_targets = "http2/1 http2/2 http2/3 http2/4 http2/5 http2/6 http2/7 http2/8 hpack" +h2spec_targets = "generic http2/3 http2/4 http2/5 http2/6 http2/7 http2/8 hpack" test_run = Test.AddTestRun() test_run.Processes.Default.Command = 'h2spec {0} -t -k --timeout 10 -p {1}'.format(h2spec_targets, ts.Variables.ssl_port) From 96d9c78e6295bc797be236e35f034e1b47453aed Mon Sep 17 00:00:00 2001 From: hankai17 <867614535@qq.com> Date: Tue, 8 Jun 2021 23:10:08 +0800 Subject: [PATCH 04/33] fix grammar (#7927) --- iocore/cache/CacheWrite.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iocore/cache/CacheWrite.cc b/iocore/cache/CacheWrite.cc index 13684c4e183..48cfe3c064f 100644 --- a/iocore/cache/CacheWrite.cc +++ b/iocore/cache/CacheWrite.cc @@ -195,7 +195,7 @@ CacheVC::updateVector(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */) - f.update. Used only if the write_vector needs to be written to disk. Used to set the length of the alternate to total_len. - write_vector. Used only if frag_type == CACHE_FRAG_TYPE_HTTP && - (f.use_fist_key || f.evac_vector) is set. Write_vector is written to disk + (f.use_first_key || f.evac_vector) is set. Write_vector is written to disk - alternate_index. Used only if write_vector needs to be written to disk. Used to find out the VC's alternate in the write_vector and set its length to tatal_len. From 6f07dcff8282b147a319f6d776e01b65f446e94c Mon Sep 17 00:00:00 2001 From: Brian Neradt Date: Tue, 8 Jun 2021 11:31:08 -0500 Subject: [PATCH 05/33] Ensure that URL components are valid when alternate eviction is logged (#7924) When alternate eviction logging takes place, we retrieve the URL. Under some circumstances the internal members of the request were invalid. This change sets a flag on the request to ensure the members are valid when the URL is printed. --- iocore/cache/CacheWrite.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iocore/cache/CacheWrite.cc b/iocore/cache/CacheWrite.cc index 48cfe3c064f..cf8d0244660 100644 --- a/iocore/cache/CacheWrite.cc +++ b/iocore/cache/CacheWrite.cc @@ -109,6 +109,10 @@ CacheVC::updateVector(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */) CacheHTTPInfo *info = write_vector->get(0); HTTPHdr *request = info->request_get(); if (request->valid()) { + // Marking the request's target as dirty will guarantee that the + // internal members of the request used for printing the URL will be + // coherent and valid by the time it is printed. + request->mark_target_dirty(); // In contrast to url_string_get, this url_print interface doesn't // use HTTPHdr's m_heap which is not valid at this point because the // HttpSM is most likely gone. From 50f2f409cea26f041b7c013164cfcd6e3c1be753 Mon Sep 17 00:00:00 2001 From: Brian Neradt Date: Tue, 8 Jun 2021 12:33:48 -0500 Subject: [PATCH 06/33] Add a chunked negative revalidating test. (#7907) This adds a negative revalidating test for a chunk encoded response. This functions as a regression test for #7880. --- .../cache/negative-revalidating.test.py | 4 +- .../negative-revalidating-enabled.replay.yaml | 118 +++++++++++++++++- 2 files changed, 114 insertions(+), 8 deletions(-) diff --git a/tests/gold_tests/cache/negative-revalidating.test.py b/tests/gold_tests/cache/negative-revalidating.test.py index 1f497520694..451e4e87a6f 100644 --- a/tests/gold_tests/cache/negative-revalidating.test.py +++ b/tests/gold_tests/cache/negative-revalidating.test.py @@ -27,7 +27,7 @@ ts = Test.MakeATSProcess("ts-negative-revalidating-disabled") ts.Disk.records_config.update({ 'proxy.config.diags.debug.enabled': 1, - 'proxy.config.diags.debug.tags': 'http', + 'proxy.config.diags.debug.tags': 'http|cache', 'proxy.config.http.insert_age_in_response': 0, 'proxy.config.http.negative_revalidating_enabled': 0, @@ -50,7 +50,7 @@ ts = Test.MakeATSProcess("ts-negative-revalidating-enabled") ts.Disk.records_config.update({ 'proxy.config.diags.debug.enabled': 1, - 'proxy.config.diags.debug.tags': 'http', + 'proxy.config.diags.debug.tags': 'http|cache', 'proxy.config.http.insert_age_in_response': 0, # Negative revalidating is on by default. Verify this by leaving out the diff --git a/tests/gold_tests/cache/replay/negative-revalidating-enabled.replay.yaml b/tests/gold_tests/cache/replay/negative-revalidating-enabled.replay.yaml index 3fdb971495f..b4b45a4b396 100644 --- a/tests/gold_tests/cache/replay/negative-revalidating-enabled.replay.yaml +++ b/tests/gold_tests/cache/replay/negative-revalidating-enabled.replay.yaml @@ -26,6 +26,9 @@ meta: sessions: - transactions: + # + # Test 1: Negative revalidating for a cached content-length response. + # - client-request: method: "GET" version: "1.1" @@ -34,7 +37,7 @@ sessions: headers: fields: - [ Host, example.com ] - - [ uuid, 1 ] + - [ uuid, 11 ] # Populate the cache with a 200 response. server-response: @@ -57,7 +60,7 @@ sessions: headers: fields: - [ Host, example.com ] - - [ uuid, 2 ] + - [ uuid, 12 ] # Give cache IO enough time to finish. delay: 100ms @@ -69,7 +72,6 @@ sessions: headers: fields: - [ Content-Length, 32 ] - - [ Cache-Control, max-age=2 ] # Again, we should serve this out of the cache. proxy-response: @@ -85,7 +87,7 @@ sessions: headers: fields: - [ Host, example.com ] - - [ uuid, 3 ] + - [ uuid, 13 ] # Make sure the item is stale per it's 2 second max-age. delay: 4s @@ -96,7 +98,6 @@ sessions: headers: fields: - [ Content-Length, 32 ] - - [ Cache-Control, max-age=2 ] # With negative_revalidating enabled, the cached response should be served # even though it is stale. @@ -112,13 +113,91 @@ sessions: headers: fields: - [ Host, example.com ] - - [ uuid, 4 ] + - [ uuid, 14 ] # After this delay, the item is 8 seconds old. This makes it: # 6 seconds beyond the server's max-age of 2 seconds and # 2 seconds beyond ATS's max_stale_age of 6 seconds. delay: 4s + server-response: + status: 503 + reason: "Service Unavailable" + headers: + fields: + - [ Content-Length, 32 ] + + # negative_revalidating is enabled, but now the cached item is older than + # max_stale_age. + proxy-response: + status: 503 + + # + # Test 2: Negative revalidating for a cached chunk encoded response. + # + # This serves as a Regression test for: + # https://github.com/apache/trafficserver/issues/7880 + # + - client-request: + method: "GET" + version: "1.1" + url: /HTTP/p + headers: + fields: + - [ Host, example.com ] + - [ uuid, 21 ] + + # Populate the cache with a 200, chunked response. + server-response: + status: 200 + reason: "OK" + headers: + fields: + - [ cache-control, max-age=2 ] + - [ Transfer-Encoding, chunked ] + + content: + size: 2072 + + proxy-response: + status: 200 + + # Verify we serve the 200 OK out of the cache if it is not stale. + - client-request: + method: "GET" + version: "1.1" + url: /HTTP/p + headers: + fields: + - [ Host, example.com ] + - [ uuid, 22 ] + + # This should not reach the origin server. + server-response: + status: 503 + reason: "Service Unavailable" + headers: + fields: + - [ Content-Length, 32 ] + + # Again, we should serve this out of the cache. + proxy-response: + status: 200 + + # Verify that with negative_revalidating enabled, we serve the 200 OK out of + # the cache even though it is stale (but younger than max_stale_age). + - client-request: + method: "GET" + version: "1.1" + url: /HTTP/p + headers: + fields: + - [ Host, example.com ] + - [ uuid, 23 ] + + # Make sure the item is stale per it's 2 second max-age. + delay: 4s + server-response: status: 503 reason: "Service Unavailable" @@ -127,6 +206,33 @@ sessions: - [ Content-Length, 32 ] - [ Cache-Control, max-age=2 ] + # With negative_revalidating enabled, the cached response should be served + # even though it is stale. + proxy-response: + status: 200 + + # Verify that max_stale_age is respected. + - client-request: + method: "GET" + version: "1.1" + url: /HTTP/p + headers: + fields: + - [ Host, example.com ] + - [ uuid, 24 ] + + # After this delay, the item is 8 seconds old. This makes it: + # 6 seconds beyond the server's max-age of 2 seconds and + # 2 seconds beyond ATS's max_stale_age of 6 seconds. + delay: 4s + + server-response: + status: 503 + reason: "Service Unavailable" + headers: + fields: + - [ Content-Length, 32 ] + # negative_revalidating is enabled, but now the cached item is older than # max_stale_age. proxy-response: From 0b6447117d976c36c7b9f49febff190f4e0e54f6 Mon Sep 17 00:00:00 2001 From: Walt Karas Date: Tue, 8 Jun 2021 12:57:15 -0500 Subject: [PATCH 07/33] Add a basic Au test using strategies.yaml, with consistent hashing. (#7911) --- .../next_hop/strategies_ch/body.gold | 1 + .../strategies_ch/strategies_ch.test.py | 141 ++++++++++++++++++ .../next_hop/strategies_ch/trace.gold | 96 ++++++++++++ 3 files changed, 238 insertions(+) create mode 100644 tests/gold_tests/next_hop/strategies_ch/body.gold create mode 100644 tests/gold_tests/next_hop/strategies_ch/strategies_ch.test.py create mode 100644 tests/gold_tests/next_hop/strategies_ch/trace.gold diff --git a/tests/gold_tests/next_hop/strategies_ch/body.gold b/tests/gold_tests/next_hop/strategies_ch/body.gold new file mode 100644 index 00000000000..b220ced9569 --- /dev/null +++ b/tests/gold_tests/next_hop/strategies_ch/body.gold @@ -0,0 +1 @@ +This is the body. diff --git a/tests/gold_tests/next_hop/strategies_ch/strategies_ch.test.py b/tests/gold_tests/next_hop/strategies_ch/strategies_ch.test.py new file mode 100644 index 00000000000..425c8d311ef --- /dev/null +++ b/tests/gold_tests/next_hop/strategies_ch/strategies_ch.test.py @@ -0,0 +1,141 @@ +''' +''' +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Test.Summary = ''' +Test next hop selection using strategies.yaml with consistent hashing. +''' + +# Define and populate MicroServer. +# +server = Test.MakeOriginServer("server") +response_header = { + "headers": + "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "Cache-control: max-age=85000\r\n" + "\r\n", + "timestamp": "1469733493.993", + "body": "This is the body.\n" +} +num_objects = 32 +for i in range(num_objects): + request_header = { + "headers": + f"GET /obj{i} HTTP/1.1\r\n" + "Host: does.not.matter\r\n" # But cannot be omitted. + "\r\n", + "timestamp": "1469733493.993", + "body": "" + } + server.addResponse("sessionlog.json", request_header, response_header) + +dns = Test.MakeDNServer("dns") + +# Define next hop trafficserver instances. +# +num_nh = 8 +ts_nh = [] +for i in range(num_nh): + ts = Test.MakeATSProcess(f"ts_nh{i}", command=f"traffic_server 2>nh_trace{i}.log") + ts.Disk.records_config.update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'http|dns', + 'proxy.config.dns.nameservers': f"127.0.0.1:{dns.Variables.Port}", + 'proxy.config.dns.resolv_conf': "NULL", + }) + ts.Disk.remap_config.AddLine( + f"map / http://127.0.0.1:{server.Variables.Port}" + ) + ts_nh.append(ts) + +ts = Test.MakeATSProcess("ts") + +ts.Disk.records_config.update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'http|dns|parent|next_hop|host_statuses|hostdb', + 'proxy.config.dns.nameservers': f"127.0.0.1:{dns.Variables.Port}", # Only nameservers if resolv_conf NULL. + 'proxy.config.dns.resolv_conf': "NULL", # This defaults to /etc/resvolv.conf (OS namesevers) if not NULL. + 'proxy.config.http.cache.http': 0, + 'proxy.config.http.uncacheable_requests_bypass_parent': 0, + 'proxy.config.http.no_dns_just_forward_to_parent': 1, + 'proxy.config.http.parent_proxy.mark_down_hostdb': 0, + 'proxy.config.http.parent_proxy.self_detect': 0, +}) + +ts.Disk.File(ts.Variables.CONFIGDIR + "/strategies.yaml", id="strategies", typename="ats:config") +s = ts.Disk.strategies +s.AddLine("groups:") +s.AddLine(" - &g1") +for i in range(num_nh): + dns.addRecords(records={f"next_hop{i}": ["127.0.0.1"]}) + s.AddLine(f" - host: next_hop{i}") + s.AddLine(f" protocol:") + s.AddLine(f" - scheme: http") + s.AddLine(f" port: {ts_nh[i].Variables.port}") + # The health check URL does not seem to be used currently. + # s.AddLine(f" health_check_url: http://next_hop{i}:{ts_nh[i].Variables.port}") + s.AddLine(f" weight: 1.0") +s.AddLines([ +"strategies:", +" - strategy: the-strategy", +" policy: consistent_hash", +" hash_key: path", +" go_direct: false", +" parent_is_proxy: true", +" ignore_self_detect: true", +" groups:", +" - *g1", +" scheme: http"]) + +# Fallover not currently tested. +# +#s.AddLines([ +#" fallover:", +#" max_simple_retries: 2", +#" ring_mode: exhaust_ring", +#" response_codes:", +#" - 404", +#" health_check:", +#" - passive"]) + +ts.Disk.remap_config.AddLine( + "map http://dummy.com http://not_used @strategy=the-strategy" +) + +tr = Test.AddTestRun() +tr.Processes.Default.StartBefore(server) +tr.Processes.Default.StartBefore(dns) +for i in range(num_nh): + tr.Processes.Default.StartBefore(ts_nh[i]) +tr.Processes.Default.StartBefore(Test.Processes.ts) +tr.Processes.Default.Command = 'echo start TS, HTTP server, DNS server and next hop TSes' +tr.Processes.Default.ReturnCode = 0 + +for i in range(num_objects): + tr = Test.AddTestRun() + tr.Processes.Default.Command = ( + f'curl --verbose --proxy 127.0.0.1:{ts.Variables.port} http://dummy.com/obj{i}' + ) + tr.Processes.Default.Streams.stdout = "body.gold" + tr.Processes.Default.ReturnCode = 0 + +tr = Test.AddTestRun() +# For some reason, the * won't be expanded when the command is executed, if stdout is not piped through "cat". +tr.Processes.Default.Command = "grep -F '200 OK' nh_trace*.log | cat" +tr.Processes.Default.Streams.stdout = "trace.gold" +tr.Processes.Default.ReturnCode = 0 diff --git a/tests/gold_tests/next_hop/strategies_ch/trace.gold b/tests/gold_tests/next_hop/strategies_ch/trace.gold new file mode 100644 index 00000000000..a02ab122967 --- /dev/null +++ b/tests/gold_tests/next_hop/strategies_ch/trace.gold @@ -0,0 +1,96 @@ +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace4.log:HTTP/1.1 200 OK +nh_trace4.log:HTTP/1.1 200 OK +nh_trace4.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace6.log:HTTP/1.1 200 OK +nh_trace6.log:HTTP/1.1 200 OK +nh_trace6.log:HTTP/1.1 200 OK +nh_trace7.log:HTTP/1.1 200 OK +nh_trace7.log:HTTP/1.1 200 OK +nh_trace7.log:HTTP/1.1 200 OK +nh_trace7.log:HTTP/1.1 200 OK +nh_trace7.log:HTTP/1.1 200 OK +nh_trace7.log:HTTP/1.1 200 OK From 69e3c492711e58f28353349919c8a24bdf94708f Mon Sep 17 00:00:00 2001 From: Brian Neradt Date: Tue, 8 Jun 2021 14:31:18 -0500 Subject: [PATCH 08/33] Satisfy ci/jenkins/bin/clang-format.sh (#7929) This has changes from `make autopep8` and dos2unix. --- .../strategies_ch/strategies_ch.test.py | 36 ++-- .../next_hop/strategies_ch/trace.gold | 192 +++++++++--------- 2 files changed, 114 insertions(+), 114 deletions(-) diff --git a/tests/gold_tests/next_hop/strategies_ch/strategies_ch.test.py b/tests/gold_tests/next_hop/strategies_ch/strategies_ch.test.py index 425c8d311ef..88c8dde1807 100644 --- a/tests/gold_tests/next_hop/strategies_ch/strategies_ch.test.py +++ b/tests/gold_tests/next_hop/strategies_ch/strategies_ch.test.py @@ -91,27 +91,27 @@ # s.AddLine(f" health_check_url: http://next_hop{i}:{ts_nh[i].Variables.port}") s.AddLine(f" weight: 1.0") s.AddLines([ -"strategies:", -" - strategy: the-strategy", -" policy: consistent_hash", -" hash_key: path", -" go_direct: false", -" parent_is_proxy: true", -" ignore_self_detect: true", -" groups:", -" - *g1", -" scheme: http"]) + "strategies:", + " - strategy: the-strategy", + " policy: consistent_hash", + " hash_key: path", + " go_direct: false", + " parent_is_proxy: true", + " ignore_self_detect: true", + " groups:", + " - *g1", + " scheme: http"]) # Fallover not currently tested. # -#s.AddLines([ -#" fallover:", -#" max_simple_retries: 2", -#" ring_mode: exhaust_ring", -#" response_codes:", -#" - 404", -#" health_check:", -#" - passive"]) +# s.AddLines([ +# " fallover:", +# " max_simple_retries: 2", +# " ring_mode: exhaust_ring", +# " response_codes:", +# " - 404", +# " health_check:", +# " - passive"]) ts.Disk.remap_config.AddLine( "map http://dummy.com http://not_used @strategy=the-strategy" diff --git a/tests/gold_tests/next_hop/strategies_ch/trace.gold b/tests/gold_tests/next_hop/strategies_ch/trace.gold index a02ab122967..929f2fa41d4 100644 --- a/tests/gold_tests/next_hop/strategies_ch/trace.gold +++ b/tests/gold_tests/next_hop/strategies_ch/trace.gold @@ -1,96 +1,96 @@ -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace0.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace1.log:HTTP/1.1 200 OK -nh_trace2.log:HTTP/1.1 200 OK -nh_trace2.log:HTTP/1.1 200 OK -nh_trace2.log:HTTP/1.1 200 OK -nh_trace2.log:HTTP/1.1 200 OK -nh_trace2.log:HTTP/1.1 200 OK -nh_trace2.log:HTTP/1.1 200 OK -nh_trace2.log:HTTP/1.1 200 OK -nh_trace2.log:HTTP/1.1 200 OK -nh_trace2.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace3.log:HTTP/1.1 200 OK -nh_trace4.log:HTTP/1.1 200 OK -nh_trace4.log:HTTP/1.1 200 OK -nh_trace4.log:HTTP/1.1 200 OK -nh_trace5.log:HTTP/1.1 200 OK -nh_trace5.log:HTTP/1.1 200 OK -nh_trace5.log:HTTP/1.1 200 OK -nh_trace5.log:HTTP/1.1 200 OK -nh_trace5.log:HTTP/1.1 200 OK -nh_trace5.log:HTTP/1.1 200 OK -nh_trace5.log:HTTP/1.1 200 OK -nh_trace5.log:HTTP/1.1 200 OK -nh_trace5.log:HTTP/1.1 200 OK -nh_trace5.log:HTTP/1.1 200 OK -nh_trace5.log:HTTP/1.1 200 OK -nh_trace5.log:HTTP/1.1 200 OK -nh_trace6.log:HTTP/1.1 200 OK -nh_trace6.log:HTTP/1.1 200 OK -nh_trace6.log:HTTP/1.1 200 OK -nh_trace7.log:HTTP/1.1 200 OK -nh_trace7.log:HTTP/1.1 200 OK -nh_trace7.log:HTTP/1.1 200 OK -nh_trace7.log:HTTP/1.1 200 OK -nh_trace7.log:HTTP/1.1 200 OK -nh_trace7.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace0.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace1.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace2.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace3.log:HTTP/1.1 200 OK +nh_trace4.log:HTTP/1.1 200 OK +nh_trace4.log:HTTP/1.1 200 OK +nh_trace4.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace5.log:HTTP/1.1 200 OK +nh_trace6.log:HTTP/1.1 200 OK +nh_trace6.log:HTTP/1.1 200 OK +nh_trace6.log:HTTP/1.1 200 OK +nh_trace7.log:HTTP/1.1 200 OK +nh_trace7.log:HTTP/1.1 200 OK +nh_trace7.log:HTTP/1.1 200 OK +nh_trace7.log:HTTP/1.1 200 OK +nh_trace7.log:HTTP/1.1 200 OK +nh_trace7.log:HTTP/1.1 200 OK From b63ea69eef31e68227a22c4b2cc86fbf1b1a9ce0 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Tue, 8 Jun 2021 16:17:59 -0500 Subject: [PATCH 09/33] Remove duplicate entry in overridable txn vars. (#7930) --- src/shared/overridable_txn_vars.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/shared/overridable_txn_vars.cc b/src/shared/overridable_txn_vars.cc index 61fd2cc5315..50fe39d3203 100644 --- a/src/shared/overridable_txn_vars.cc +++ b/src/shared/overridable_txn_vars.cc @@ -48,6 +48,7 @@ const std::unordered_map Date: Wed, 9 Jun 2021 07:39:40 -0600 Subject: [PATCH 10/33] regex_reval: bug where rule type is always reported as the first (#7928) --- plugins/regex_revalidate/regex_revalidate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/regex_revalidate/regex_revalidate.c b/plugins/regex_revalidate/regex_revalidate.c index 56241355809..01711f1a973 100644 --- a/plugins/regex_revalidate/regex_revalidate.c +++ b/plugins/regex_revalidate/regex_revalidate.c @@ -341,7 +341,7 @@ list_config(plugin_state_t *pstate, invalidate_t *i) if (i) { iptr = i; while (iptr) { - char const *const typestr = strForResult(i->new_result); + char const *const typestr = strForResult(iptr->new_result); TSDebug(LOG_PREFIX, "%s epoch: %d expiry: %d result: %s", iptr->regex_text, (int)iptr->epoch, (int)iptr->expiry, typestr); if (pstate->log) { TSTextLogObjectWrite(pstate->log, "%s epoch: %d expiry: %d result: %s", iptr->regex_text, (int)iptr->epoch, From b85d9fd8481bc55547aade92f5b9cf7a8db5099f Mon Sep 17 00:00:00 2001 From: Susan Hinrichs Date: Wed, 9 Jun 2021 09:58:56 -0500 Subject: [PATCH 11/33] Fix race condition on server session state (#7921) --- proxy/http/Http1ServerSession.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/proxy/http/Http1ServerSession.cc b/proxy/http/Http1ServerSession.cc index 764cd810a14..bcd09acb509 100644 --- a/proxy/http/Http1ServerSession.cc +++ b/proxy/http/Http1ServerSession.cc @@ -228,8 +228,6 @@ Http1ServerSession ::release_transaction() // The session was successfully put into the session // manager and it will manage it // (Note: should never get HSM_NOT_FOUND here) - // Set our state to KA for stat issues - state = KA_POOLED; ink_assert(r == HSM_DONE); // If the session got picked up immediately by another thread the transact_count could be greater ink_release_assert(transact_count >= released_transactions); From 4e08a4f93adc0076f390a5ed9b0df82370e44774 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Tue, 11 May 2021 13:13:11 -0500 Subject: [PATCH 12/33] Rebase checkpoint for HostDB restructure. --- configs/body_factory/default/Makefile.am | 1 + configs/body_factory/default/connect#all_dead | 17 + .../core-architecture/hostdb.en.rst | 40 + doc/uml/host-resolve.plantuml | 24 + example/plugins/c-api/protocol/TxnSM.c | 14 +- include/ts/ts.h | 9 +- include/tscore/BufferWriterForward.h | 7 + include/tscore/bwf_std_format.h | 16 +- include/tscore/ts_file.h | 7 + iocore/dns/P_SplitDNSProcessor.h | 36 +- iocore/dns/SRV.h | 1 - iocore/dns/SplitDNS.cc | 6 +- iocore/hostdb/HostDB.cc | 1612 +++++++++-------- iocore/hostdb/I_HostDBProcessor.h | 942 ++++++---- iocore/hostdb/P_HostDBProcessor.h | 278 +-- plugins/lua/ts_lua_misc.c | 12 +- proxy/ControlMatcher.h | 8 +- proxy/ParentSelection.cc | 30 +- proxy/http/HttpConfig.cc | 4 +- proxy/http/HttpConfig.h | 4 +- proxy/http/HttpConnectionCount.cc | 2 +- proxy/http/HttpSM.cc | 359 ++-- proxy/http/HttpSM.h | 7 +- proxy/http/HttpTransact.cc | 349 ++-- proxy/http/HttpTransact.h | 78 +- .../remap/unit-tests/nexthop_test_stubs.cc | 6 +- src/traffic_server/InkAPI.cc | 27 +- src/traffic_server/InkAPITest.cc | 8 +- 28 files changed, 1987 insertions(+), 1917 deletions(-) create mode 100644 configs/body_factory/default/connect#all_dead create mode 100644 doc/developer-guide/core-architecture/hostdb.en.rst create mode 100644 doc/uml/host-resolve.plantuml diff --git a/configs/body_factory/default/Makefile.am b/configs/body_factory/default/Makefile.am index a24d2e290dc..69eb6b6810f 100644 --- a/configs/body_factory/default/Makefile.am +++ b/configs/body_factory/default/Makefile.am @@ -28,6 +28,7 @@ dist_bodyfactory_DATA = \ connect\#dns_failed \ connect\#failed_connect \ connect\#hangup \ + connect\#all_dead \ default \ interception\#no_host \ README \ diff --git a/configs/body_factory/default/connect#all_dead b/configs/body_factory/default/connect#all_dead new file mode 100644 index 00000000000..7e18a62986f --- /dev/null +++ b/configs/body_factory/default/connect#all_dead @@ -0,0 +1,17 @@ + + +No Valid Host + + + +

No Valid Host

+
+ + +Description: Unable to find a valid target host. + +The server was found but all of the addresses are marked dead and so there is +no valid target address to which to connect. Please try again after a few minutes. + +
+ diff --git a/doc/developer-guide/core-architecture/hostdb.en.rst b/doc/developer-guide/core-architecture/hostdb.en.rst new file mode 100644 index 00000000000..fa3097be420 --- /dev/null +++ b/doc/developer-guide/core-architecture/hostdb.en.rst @@ -0,0 +1,40 @@ +.. Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +.. include:: ../../common.defs + +.. highlight:: cpp +.. default-domain:: cpp + +.. _developer-doc-hostdb: + +HostDB +****** + +HostDB is a cache of DNS results. It is used to increase performance by aggregating address +resolution across transactions. + +Runtime Structure +================= + +DNS results are stored in a global hash table as instances of ``HostDBRecord``. Each instance +stores the results of a single query. These records are not updated with new DNS results. Instead +a new record instance is created and replaces the previous instance in the table. Some specific +dynamic data is migrated from the old record to the new one, such as the failure status of the +upstreams in the record. + + diff --git a/doc/uml/host-resolve.plantuml b/doc/uml/host-resolve.plantuml new file mode 100644 index 00000000000..f3c6a6091e9 --- /dev/null +++ b/doc/uml/host-resolve.plantuml @@ -0,0 +1,24 @@ +' SPDX-License-Identifier: Apache-2.0 +' Licensed under the Apache License, Version 2.0 (the "License"); +' you may not use this file except in compliance with the License. +' You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +' Unless required by applicable law or agreed to in writing, software distributed under the License is distributed +' on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +' See the License for the specific language governing permissions and limitations under the License. + +@startuml + +hide empty description + +state HttpSM { + state do_http_server_open { + } +} + +state HandleRequest #cyan +state CallOSDNSLookup #cyan + +CallOSDNSLookup -> OSDNSLookup + +@enduml + diff --git a/example/plugins/c-api/protocol/TxnSM.c b/example/plugins/c-api/protocol/TxnSM.c index 8f6ae5416a6..cbd85122af3 100644 --- a/example/plugins/c-api/protocol/TxnSM.c +++ b/example/plugins/c-api/protocol/TxnSM.c @@ -477,8 +477,8 @@ int state_dns_lookup(TSCont contp, TSEvent event, TSHostLookupResult host_info) { TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); - struct sockaddr const *q_server_addr; - struct sockaddr_in ip_addr; + struct sockaddr_storage q_server_addr; + struct sockaddr_in *addr; TSDebug(PLUGIN_NAME, "enter state_dns_lookup"); @@ -489,16 +489,16 @@ state_dns_lookup(TSCont contp, TSEvent event, TSHostLookupResult host_info) txn_sm->q_pending_action = NULL; /* Get the server IP from data structure TSHostLookupResult. */ - q_server_addr = TSHostLookupResultAddrGet(host_info); + TSHostLookupResultAddrGet(host_info, (struct sockaddr *)&q_server_addr); /* Connect to the server using its IP. */ set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_connect_to_server); TSAssert(txn_sm->q_pending_action == NULL); - TSAssert(q_server_addr->sa_family == AF_INET); /* NO IPv6 in this plugin */ + TSAssert(q_server_addr.ss_family == AF_INET); /* NO IPv6 in this plugin */ + addr = (struct sockaddr_in *)(&q_server_addr); - memcpy(&ip_addr, q_server_addr, sizeof(ip_addr)); - ip_addr.sin_port = txn_sm->q_server_port; - txn_sm->q_pending_action = TSNetConnect(contp, (struct sockaddr const *)&ip_addr); + addr->sin_port = txn_sm->q_server_port; + txn_sm->q_pending_action = TSNetConnect(contp, (struct sockaddr const *)addr); return TS_SUCCESS; } diff --git a/include/ts/ts.h b/include/ts/ts.h index 61e7d45e251..64246a0839f 100644 --- a/include/ts/ts.h +++ b/include/ts/ts.h @@ -1918,7 +1918,14 @@ tsapi TSReturnCode TSPortDescriptorAccept(TSPortDescriptor, TSCont); /* -------------------------------------------------------------------------- DNS Lookups */ tsapi TSAction TSHostLookup(TSCont contp, const char *hostname, size_t namelen); -tsapi struct sockaddr const *TSHostLookupResultAddrGet(TSHostLookupResult lookup_result); +/** Retrieve an address from the host lookup. + * + * @param lookup_result Result handle passed to event callback. + * @param dst Destiation for copying the addres. + * @return @c TS_SUCCESS if the address was available and copied, @c TS_ERROR otherwise. + */ +tsapi TSReturnCode TSHostLookupResultAddrGet(TSHostLookupResult lookup_result, struct sockaddr *dst); + /* TODO: Eventually, we might want something like this as well, but it requires support for building the HostDBInfo struct: tsapi void TSHostLookupResultSet(TSHttpTxn txnp, TSHostLookupResult result); diff --git a/include/tscore/BufferWriterForward.h b/include/tscore/BufferWriterForward.h index 50faa9ecdc1..dd1cfe2de64 100644 --- a/include/tscore/BufferWriterForward.h +++ b/include/tscore/BufferWriterForward.h @@ -147,4 +147,11 @@ class BWFormat; class BufferWriter; +/// Storage for debug messages. +/// If @c bwprint is used with this, the storage is reused which minimizes allocations. +/// E.g. +/// @code + +inline thread_local std::string bw_dbg; + } // namespace ts diff --git a/include/tscore/bwf_std_format.h b/include/tscore/bwf_std_format.h index e67c858fc0b..cb060edd170 100644 --- a/include/tscore/bwf_std_format.h +++ b/include/tscore/bwf_std_format.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "tscpp/util/TextView.h" #include "tscore/BufferWriterForward.h" @@ -38,6 +39,20 @@ bwformat(ts::BufferWriter &w, ts::BWFSpec const &spec, atomic const &v) return ts::bwformat(w, spec, v.load()); } +template +ts::BufferWriter & +bwformat(ts::BufferWriter &w, ts::BWFSpec const &spec, chrono::duration const &d) +{ + return bwformat(w, spec, d.count()); +} + +template +ts::BufferWriter & +bwformat(ts::BufferWriter &w, ts::BWFSpec const &spec, chrono::time_point const &t) +{ + return bwformat(w, spec, t.time_since_epoch()); +} + } // end namespace std namespace ts @@ -130,5 +145,4 @@ namespace bwf BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, bwf::Errno const &e); BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, bwf::Date const &date); BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, bwf::OptionalAffix const &opts); - } // namespace ts diff --git a/include/tscore/ts_file.h b/include/tscore/ts_file.h index c4389e948f6..8a9eff2befc 100644 --- a/include/tscore/ts_file.h +++ b/include/tscore/ts_file.h @@ -329,5 +329,12 @@ namespace file /* ------------------------------------------------------------------- */ } // namespace file + +inline BufferWriter & +bwformat(BufferWriter &w, BWFSpec const &spec, file::path const &path) +{ + return bwformat(w, spec, path.string()); +} + } // namespace ts /* ------------------------------------------------------------------- */ diff --git a/iocore/dns/P_SplitDNSProcessor.h b/iocore/dns/P_SplitDNSProcessor.h index 5fa119e5f5a..7424ccfdc7b 100644 --- a/iocore/dns/P_SplitDNSProcessor.h +++ b/iocore/dns/P_SplitDNSProcessor.h @@ -79,7 +79,7 @@ struct SplitDNS : public ConfigInfo { SplitDNS(); ~SplitDNS() override; - void *getDNSRecord(const char *hostname); + void *getDNSRecord(ts::TextView hostname); void findServer(RequestData *rdata, SplitDNSResult *result); DNS_table *m_DNSSrvrTable = nullptr; @@ -116,46 +116,34 @@ SplitDNSConfig::isSplitDNSEnabled() class DNSRequestData : public RequestData { public: - DNSRequestData(); - - char *get_string() override; + DNSRequestData() = default; + char * + get_string() override + { + ink_release_assert(!"Do not get a writeable string from a DNS request"); + }; const char *get_host() override; sockaddr const *get_ip() override; // unused required virtual method. sockaddr const *get_client_ip() override; // unused required virtual method. - const char *m_pHost = nullptr; + ts::TextView m_pHost; }; -/* -------------------------------------------------------------- - DNSRequestData::get_string() - -------------------------------------------------------------- */ -TS_INLINE -DNSRequestData::DNSRequestData() {} - -/* -------------------------------------------------------------- - DNSRequestData::get_string() - -------------------------------------------------------------- */ -TS_INLINE char * -DNSRequestData::get_string() -{ - return ats_strdup((char *)m_pHost); -} - /* -------------------------------------------------------------- DNSRequestData::get_host() -------------------------------------------------------------- */ -TS_INLINE const char * +inline const char * DNSRequestData::get_host() { - return m_pHost; + return m_pHost.data(); } /* -------------------------------------------------------------- DNSRequestData::get_ip() -------------------------------------------------------------- */ -TS_INLINE sockaddr const * +inline sockaddr const * DNSRequestData::get_ip() { return nullptr; @@ -164,7 +152,7 @@ DNSRequestData::get_ip() /* -------------------------------------------------------------- DNSRequestData::get_client_ip() -------------------------------------------------------------- */ -TS_INLINE sockaddr const * +inline sockaddr const * DNSRequestData::get_client_ip() { return nullptr; diff --git a/iocore/dns/SRV.h b/iocore/dns/SRV.h index ff75689e74e..560223636e4 100644 --- a/iocore/dns/SRV.h +++ b/iocore/dns/SRV.h @@ -25,7 +25,6 @@ #include #include "tscore/ink_platform.h" -#include "I_HostDBProcessor.h" struct HostDBInfo; diff --git a/iocore/dns/SplitDNS.cc b/iocore/dns/SplitDNS.cc index 802e97981cd..b993f1e7f51 100644 --- a/iocore/dns/SplitDNS.cc +++ b/iocore/dns/SplitDNS.cc @@ -178,9 +178,9 @@ SplitDNSConfig::print() SplitDNS::getDNSRecord() -------------------------------------------------------------- */ void * -SplitDNS::getDNSRecord(const char *hostname) +SplitDNS::getDNSRecord(ts::TextView hostname) { - Debug("splitdns", "Called SplitDNS::getDNSRecord(%s)", hostname); + Debug("splitdns", "Called SplitDNS::getDNSRecord(%.*s)", int(hostname.size()), hostname.data()); DNSRequestData *pRD = DNSReqAllocator.alloc(); pRD->m_pHost = hostname; @@ -191,7 +191,7 @@ SplitDNS::getDNSRecord(const char *hostname) DNSReqAllocator.free(pRD); if (DNS_SRVR_SPECIFIED == res.r) { - return (void *)&(res.m_rec->m_servers); + return &(res.m_rec->m_servers); } Debug("splitdns", "Fail to match a valid splitdns rule, fallback to default dns resolver"); diff --git a/iocore/hostdb/HostDB.cc b/iocore/hostdb/HostDB.cc index 6589b211872..6f1843ef418 100644 --- a/iocore/hostdb/HostDB.cc +++ b/iocore/hostdb/HostDB.cc @@ -26,14 +26,18 @@ #include "P_RefCountCacheSerializer.h" #include "tscore/I_Layout.h" #include "Show.h" -#include "tscore/Tokenizer.h" +#include "tscore/ts_file.h" #include "tscore/ink_apidefs.h" +#include "tscore/bwf_std_format.h" #include #include #include #include #include +#include + +using ts::TextView; HostDBProcessor hostDBProcessor; int HostDBProcessor::hostdb_strict_round_robin = 0; @@ -50,78 +54,214 @@ unsigned int hostdb_ip_stale_interval = HOST_DB_IP_STALE; unsigned int hostdb_ip_timeout_interval = HOST_DB_IP_TIMEOUT; unsigned int hostdb_ip_fail_timeout_interval = HOST_DB_IP_FAIL_TIMEOUT; unsigned int hostdb_serve_stale_but_revalidate = 0; -unsigned int hostdb_hostfile_check_interval = 86400; // 1 day +ts_seconds hostdb_hostfile_check_interval{std::chrono::hours(24)}; // Epoch timestamp of the current hosts file check. -ink_time_t hostdb_current_interval = 0; +ts_time hostdb_current_interval{TS_TIME_ZERO}; // Epoch timestamp of the last time we actually checked for a hosts file update. -static ink_time_t hostdb_last_interval = 0; +static ts_time hostdb_last_interval{TS_TIME_ZERO}; // Epoch timestamp when we updated the hosts file last. -static ink_time_t hostdb_hostfile_update_timestamp = 0; -static char hostdb_filename[PATH_NAME_MAX] = DEFAULT_HOST_DB_FILENAME; -int hostdb_max_count = DEFAULT_HOST_DB_SIZE; -char hostdb_hostfile_path[PATH_NAME_MAX] = ""; -int hostdb_sync_frequency = 0; -int hostdb_disable_reverse_lookup = 0; -int hostdb_max_iobuf_index = BUFFER_SIZE_INDEX_32K; - -// Verify the generic storage is sufficient to cover all alternate members. -static_assert(sizeof(HostDBApplicationInfo::allotment) == sizeof(HostDBApplicationInfo), - "Generic storage for HostDBApplicationInfo is smaller than the union storage."); +static ts_time hostdb_hostfile_update_timestamp{TS_TIME_ZERO}; +static char hostdb_filename[PATH_NAME_MAX] = DEFAULT_HOST_DB_FILENAME; +int hostdb_max_count = DEFAULT_HOST_DB_SIZE; +static ts::file::path hostdb_hostfile_path; +ts_seconds hostdb_sync_frequency{0}; +int hostdb_disable_reverse_lookup = 0; +int hostdb_max_iobuf_index = BUFFER_SIZE_INDEX_32K; ClassAllocator hostDBContAllocator("hostDBContAllocator"); +namespace +{ +/** Assign raw storage to an @c IpAddr + * + * @param ip Destination. + * @param af IP family. + * @param ptr Raw data for an address of family @a af. + */ +void +ip_addr_set(IpAddr &ip, ///< Target storage. + uint8_t af, ///< Address format. + void const *ptr ///< Raw address data +) +{ + if (AF_INET6 == af) { + ip = *static_cast(ptr); + } else if (AF_INET == af) { + ip = *static_cast(ptr); + } else { + ip.invalidate(); + } +} + +unsigned int +HOSTDB_CLIENT_IP_HASH(sockaddr const *lhs, IpAddr const &rhs) +{ + unsigned int zret = ~static_cast(0); + if (lhs->sa_family == rhs.family()) { + if (rhs.isIp4()) { + in_addr_t ip1 = ats_ip4_addr_cast(lhs); + in_addr_t ip2 = rhs._addr._ip4; + zret = (ip1 >> 16) ^ ip1 ^ ip2 ^ (ip2 >> 16); + } else if (rhs.isIp6()) { + uint32_t const *ip1 = ats_ip_addr32_cast(lhs); + uint32_t const *ip2 = rhs._addr._u32; + for (int i = 0; i < 4; ++i, ++ip1, ++ip2) { + zret ^= (*ip1 >> 16) ^ *ip1 ^ *ip2 ^ (*ip2 >> 16); + } + } + } + return zret & 0xFFFF; +} + +} // namespace + +char const * +name_of(HostDBType t) +{ + switch (t) { + case HostDBType::UNSPEC: + return "*"; + case HostDBType::ADDR: + return "Address"; + case HostDBType::SRV: + return "SRV"; + case HostDBType::HOST: + return "Reverse DNS"; + } + return ""; +} + +/** Template for creating conversions and initialization for @c std::chrono based configuration variables. + * + * @tparam V The exact type of the configuration variable. + * + * The tricky template code is to enable having a class instance for each configuration variable, instead of for each _type_ of + * configuration variable. This is required because the callback interface requires functions and so the actual storage must be + * accessible from that function. * + */ +template struct ConfigDuration { + using self_type = ConfigDuration; + V *_var; ///< Pointer to the variable to control. + + /** Constructor. + * + * @param v The variable to update. + */ + ConfigDuration(V &v) : _var(&v) {} + + /// Convert to the mgmt (configuration) type. + static MgmtInt + to_mgmt(void const *data) + { + return static_cast(static_cast(data)->count()); + } + + /// Convert from the mgmt (configuration) type. + static void + from_mgmt(void *data, MgmtInt i) + { + *static_cast(data) = V{i}; + } + + /// The conversion structure, which handles @c MgmtInt. + static inline const MgmtConverter Conversions{&to_mgmt, &from_mgmt}; + + /** Process start up conversion from configuration. + * + * @param type The data type in the configuration. + * @param data The data in the configuration. + * @param var Pointer to the variable to update. + * @return @c true if @a data was successfully converted and stored, @c false if not. + * + * @note @a var is the target variable because it was explicitly set to be the value of @a _var in @c Enable. + */ + static bool + callback(char const *, RecDataT type, RecData data, void *var) + { + if (RECD_INT == type) { + (*self_type::Conversions.store_int)(var, data.rec_int); + return true; + } + return false; + } + + /** Enable. + * + * @param name Name of the configuration variable. + * + * This enables both reading from the configuration and handling the callback for dynamic + * updates of the variable. + */ + void + Enable(std::string_view name) + { + Enable_Config_Var(name, &self_type::callback, _var); + } +}; + +ConfigDuration HostDBDownServerCacheTimeVar{HttpConfig::m_master.oride.down_server_timeout}; +// Make the conversions visible to the plugin API. This allows exporting just the conversions +// without having to export the class definition. Again, the compiler doesn't allow doing this +// in one line. +extern MgmtConverter const &HostDBDownServerCacheTimeConv; +MgmtConverter const &HostDBDownServerCacheTimeConv = HostDBDownServerCacheTimeVar.Conversions; + +// Not run time configurable, therefore no support beyond this class needed. +ConfigDuration HostDBSyncFrequency{hostdb_sync_frequency}; + +void +HostDB_Config_Init() +{ + HostDBDownServerCacheTimeVar.Enable("proxy.config.http.down_server.cache_time"); +} + // Static configuration information HostDBCache hostDB; -void ParseHostFile(const char *path, unsigned int interval); +void ParseHostFile(ts::file::path const &path, ts_seconds interval); -char * -HostDBInfo::srvname(HostDBRoundRobin *rr) const +auto +HostDBInfo::assign(sa_family_t af, void const *addr) -> self_type & { - if (!is_srv || !data.srv.srv_offset) { - return nullptr; - } - return reinterpret_cast(rr) + data.srv.srv_offset; + type = HostDBType::ADDR; + ip_addr_set(data.ip, af, addr); + return *this; } -static inline bool -is_addr_valid(uint8_t af, ///< Address family (format of data) - void *ptr ///< Raw address data (not a sockaddr variant!) -) +auto +HostDBInfo::assign(IpAddr const &addr) -> self_type & { - return (AF_INET == af && INADDR_ANY != *(reinterpret_cast(ptr))) || - (AF_INET6 == af && !IN6_IS_ADDR_UNSPECIFIED(reinterpret_cast(ptr))); + type = HostDBType::ADDR; + data.ip = addr; + return *this; } -static inline void -ip_addr_set(sockaddr *ip, ///< Target storage, sockaddr compliant. - uint8_t af, ///< Address format. - void *ptr ///< Raw address data -) +auto +HostDBInfo::assign(SRV const *srv, char const *name) -> self_type & { - if (AF_INET6 == af) { - ats_ip6_set(ip, *static_cast(ptr)); - } else if (AF_INET == af) { - ats_ip4_set(ip, *static_cast(ptr)); - } else { - ats_ip_invalidate(ip); - } + type = HostDBType::SRV; + data.srv.srv_weight = srv->weight; + data.srv.srv_priority = srv->priority; + data.srv.srv_port = srv->port; + data.srv.key = srv->key; + data.srv.srv_offset = reinterpret_cast(this) - name; + return *this; +} + +char const * +HostDBInfo::srvname() const +{ + return data.srv.srv_offset ? reinterpret_cast(this) + data.srv.srv_offset : nullptr; } -static inline void -ip_addr_set(IpAddr &ip, ///< Target storage. - uint8_t af, ///< Address format. - void *ptr ///< Raw address data +static inline bool +is_addr_valid(uint8_t af, ///< Address family (format of data) + void *ptr ///< Raw address data (not a sockaddr variant!) ) { - if (AF_INET6 == af) { - ip = *static_cast(ptr); - } else if (AF_INET == af) { - ip = *static_cast(ptr); - } else { - ip.invalidate(); - } + return (AF_INET == af && INADDR_ANY != *(reinterpret_cast(ptr))) || + (AF_INET6 == af && !IN6_IS_ADDR_UNSPECIFIED(reinterpret_cast(ptr))); } inline void @@ -169,18 +309,12 @@ string_for(HostDBMark mark) static Action *register_ShowHostDB(Continuation *c, HTTPHdr *h); HostDBHash & -HostDBHash::set_host(const char *name, int len) +HostDBHash::set_host(TextView name) { host_name = name; - host_len = len; - if (host_name && SplitDNSConfig::isSplitDNSEnabled()) { - const char *scan; - // I think this is checking for a hostname that is just an address. - for (scan = host_name; *scan != '\0' && (ParseRules::is_digit(*scan) || '.' == *scan || ':' == *scan); ++scan) { - ; - } - if ('\0' != *scan) { + if (!host_name.empty() && SplitDNSConfig::isSplitDNSEnabled()) { + if (TS_SUCCESS != ip.load(host_name)) { // config is released in the destructor, because we must make sure values we // get out of it don't evaporate while @a this is still around. if (!pSD) { @@ -206,7 +340,7 @@ HostDBHash::refresh() const char *server_line = dns_server ? dns_server->x_dns_ip_line : nullptr; uint8_t m = static_cast(db_mark); // be sure of the type. - ctx.update(host_name, host_len); + ctx.update(host_name.data(), host_name.size()); ctx.update(reinterpret_cast(&port), sizeof(port)); ctx.update(&m, sizeof(m)); if (server_line) { @@ -235,10 +369,7 @@ HostDBHash::~HostDBHash() } } -HostDBCache::HostDBCache() -{ - hosts_file_ptr = new RefCountedHostsFileMap(); -} +HostDBCache::HostDBCache() {} bool HostDBCache::is_pending_dns_for_hash(const CryptoHash &hash) @@ -252,6 +383,14 @@ HostDBCache::is_pending_dns_for_hash(const CryptoHash &hash) return false; } +std::shared_ptr +HostDBCache::acquire_host_file() +{ + std::shared_lock lock(host_file_mutex); + auto zret = host_file; + return zret; +} + HostDBCache * HostDBProcessor::cache() { @@ -259,16 +398,16 @@ HostDBProcessor::cache() } struct HostDBBackgroundTask : public Continuation { - int frequency; - ink_hrtime start_time; + ts_seconds frequency; + ts_hr_time start_time; virtual int sync_event(int event, void *edata) = 0; int wait_event(int event, void *edata); - HostDBBackgroundTask(int frequency); + HostDBBackgroundTask(ts_seconds frequency); }; -HostDBBackgroundTask::HostDBBackgroundTask(int frequency) : Continuation(new_ProxyMutex()), frequency(frequency), start_time(0) +HostDBBackgroundTask::HostDBBackgroundTask(ts_seconds frequency) : Continuation(new_ProxyMutex()), frequency(frequency) { SET_HANDLER(&HostDBBackgroundTask::sync_event); } @@ -276,11 +415,11 @@ HostDBBackgroundTask::HostDBBackgroundTask(int frequency) : Continuation(new_Pro int HostDBBackgroundTask::wait_event(int, void *) { - ink_hrtime next_sync = HRTIME_SECONDS(this->frequency) - (Thread::get_hrtime() - start_time); + auto next_sync = this->frequency - (ts_hr_clock::now() - start_time); SET_HANDLER(&HostDBBackgroundTask::sync_event); - if (next_sync > HRTIME_MSECONDS(100)) { - eventProcessor.schedule_in(this, next_sync, ET_TASK); + if (next_sync > ts_milliseconds{100}) { + eventProcessor.schedule_in(this, std::chrono::duration_cast(next_sync).count(), ET_TASK); } else { eventProcessor.schedule_imm(this, ET_TASK); } @@ -290,16 +429,16 @@ HostDBBackgroundTask::wait_event(int, void *) struct HostDBSync : public HostDBBackgroundTask { std::string storage_path; std::string full_path; - HostDBSync(int frequency, const std::string &storage_path, const std::string &full_path) + HostDBSync(ts_seconds frequency, const std::string &storage_path, const std::string &full_path) : HostDBBackgroundTask(frequency), storage_path(std::move(storage_path)), full_path(std::move(full_path)){}; int sync_event(int, void *) override { SET_HANDLER(&HostDBSync::wait_event); - start_time = Thread::get_hrtime(); + start_time = ts_hr_clock::now(); - new RefCountCacheSerializer(this, hostDBProcessor.cache()->refcountcache, this->frequency, this->storage_path, - this->full_path); + new RefCountCacheSerializer(this, hostDBProcessor.cache()->refcountcache, this->frequency.count(), + this->storage_path, this->full_path); return EVENT_DONE; } }; @@ -327,8 +466,6 @@ HostDBCache::start(int flags) REC_ReadConfigInteger(hostdb_max_size, "proxy.config.hostdb.max_size"); // number of partitions REC_ReadConfigInt32(hostdb_partitions, "proxy.config.hostdb.partitions"); - // how often to sync hostdb to disk - REC_EstablishStaticConfigInt32(hostdb_sync_frequency, "proxy.config.cache.hostdb.sync_frequency"); REC_EstablishStaticConfigInt32(hostdb_max_iobuf_index, "proxy.config.hostdb.io.max_buffer_index"); @@ -337,13 +474,13 @@ HostDBCache::start(int flags) } // Setup the ref-counted cache (this must be done regardless of syncing or not). - this->refcountcache = new RefCountCache(hostdb_partitions, hostdb_max_size, hostdb_max_count, HostDBInfo::version(), - "proxy.process.hostdb.cache."); + this->refcountcache = new RefCountCache(hostdb_partitions, hostdb_max_size, hostdb_max_count, HostDBRecord::Version, + "proxy.process.hostdb.cache."); // // Load and sync HostDB, if we've asked for it. // - if (hostdb_sync_frequency > 0) { + if (hostdb_sync_frequency.count() > 0) { // If proxy.config.hostdb.storage_path is not set, use the local state dir. If it is set to // a relative path, make it relative to the prefix. if (storage_path[0] == '\0') { @@ -366,7 +503,7 @@ HostDBCache::start(int flags) Debug("hostdb", "Opening %s, partitions=%d storage_size=%" PRIu64 " items=%d", full_path, hostdb_partitions, hostdb_max_size, hostdb_max_count); - int load_ret = LoadRefCountCacheFromPath(*this->refcountcache, storage_path, full_path, HostDBInfo::unmarshall); + int load_ret = LoadRefCountCacheFromPath(*this->refcountcache, storage_path, full_path, HostDBRecord::unmarshall); if (load_ret != 0) { Warning("Error loading cache from %s: %d", full_path, load_ret); } @@ -411,13 +548,12 @@ HostDBProcessor::start(int, size_t) REC_EstablishStaticConfigInt32U(hostdb_ip_stale_interval, "proxy.config.hostdb.verify_after"); REC_EstablishStaticConfigInt32U(hostdb_ip_fail_timeout_interval, "proxy.config.hostdb.fail.timeout"); REC_EstablishStaticConfigInt32U(hostdb_serve_stale_but_revalidate, "proxy.config.hostdb.serve_stale_for"); - REC_EstablishStaticConfigInt32U(hostdb_hostfile_check_interval, "proxy.config.hostdb.host_file.interval"); REC_EstablishStaticConfigInt32U(hostdb_round_robin_max_count, "proxy.config.hostdb.round_robin_max_count"); // // Set up hostdb_current_interval // - hostdb_current_interval = ink_time(); + hostdb_current_interval = ts_clock::now(); HostDBContinuation *b = hostDBContAllocator.alloc(); SET_CONTINUATION_HANDLER(b, (HostDBContHandler)&HostDBContinuation::backgroundEvent); @@ -430,18 +566,14 @@ HostDBProcessor::start(int, size_t) void HostDBContinuation::init(HostDBHash const &the_hash, Options const &opt) { - hash = the_hash; - if (hash.host_name) { + hash = the_hash; + hash.host_name = hash.host_name.prefix(static_cast(sizeof(hash_host_name_store) - 1)); + if (!hash.host_name.empty()) { // copy to backing store. - if (hash.host_len > static_cast(sizeof(hash_host_name_store) - 1)) { - hash.host_len = sizeof(hash_host_name_store) - 1; - } - memcpy(hash_host_name_store, hash.host_name, hash.host_len); - } else { - hash.host_len = 0; + memcpy(hash_host_name_store, hash.host_name); } - hash_host_name_store[hash.host_len] = 0; - hash.host_name = hash_host_name_store; + hash_host_name_store[hash.host_name.size()] = 0; + hash.host_name.assign(hash_host_name_store, hash.host_name.size()); host_res_style = opt.host_res_style; dns_lookup_timeout = opt.timeout; @@ -470,34 +602,22 @@ HostDBContinuation::refresh_hash() } static bool -reply_to_cont(Continuation *cont, HostDBInfo *r, bool is_srv = false) +reply_to_cont(Continuation *cont, HostDBRecord *r, bool is_srv = false) { - if (r == nullptr || r->is_srv != is_srv || r->is_failed()) { + if (r == nullptr || r->is_srv() != is_srv || r->is_failed()) { cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, nullptr); return false; } - if (r->reverse_dns) { - if (!r->hostname()) { + if (r->record_type != HostDBType::HOST) { + if (!r->name()) { ink_assert(!"missing hostname"); cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, nullptr); Warning("bogus entry deleted from HostDB: missing hostname"); hostDB.refcountcache->erase(r->key); return false; } - Debug("hostdb", "hostname = %s", r->hostname()); - } - - if (!r->is_srv && r->round_robin) { - if (!r->rr()) { - ink_assert(!"missing round-robin"); - cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, nullptr); - Warning("bogus entry deleted from HostDB: missing round-robin"); - hostDB.refcountcache->erase(r->key); - return false; - } - ip_text_buffer ipb; - Debug("hostdb", "RR of %d with %d good, 1st IP = %s", r->rr()->rrcount, r->rr()->good, ats_ip_ntop(r->ip(), ipb, sizeof ipb)); + Debug("hostdb", "hostname = %s", r->name()); } cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, r); @@ -541,73 +661,57 @@ db_mark_for(IpAddr const &ip) return ip.isIp6() ? HOSTDB_MARK_IPV6 : HOSTDB_MARK_IPV4; } -Ptr +HostDBRecord::Handle probe(const Ptr &mutex, HostDBHash const &hash, bool ignore_timeout) { + static const Ptr NO_RECORD; + // If hostdb is disabled, don't return anything if (!hostdb_enable) { - return Ptr(); + return NO_RECORD; } // Otherwise HostDB is enabled, so we'll do our thing ink_assert(this_ethread() == hostDB.refcountcache->lock_for_key(hash.hash.fold())->thread_holding); uint64_t folded_hash = hash.hash.fold(); - // get the item from cache - Ptr r = hostDB.refcountcache->get(folded_hash); + // get the record from cache + Ptr record = hostDB.refcountcache->get(folded_hash); // If there was nothing in the cache-- this is a miss - if (r.get() == nullptr) { - return r; + if (record.get() == nullptr) { + return record; } // If the dns response was failed, and we've hit the failed timeout, lets stop returning it - if (r->is_failed() && r->is_ip_fail_timeout()) { - return make_ptr((HostDBInfo *)nullptr); - // if we aren't ignoring timeouts, and we are past it-- then remove the item - } else if (!ignore_timeout && r->is_ip_timeout() && !r->serve_stale_but_revalidate()) { + if (record->is_failed() && record->is_ip_fail_timeout()) { + return NO_RECORD; + // if we aren't ignoring timeouts, and we are past it-- then remove the record + } else if (!ignore_timeout && record->is_ip_timeout() && !record->serve_stale_but_revalidate()) { HOSTDB_INCREMENT_DYN_STAT(hostdb_ttl_expires_stat); - return make_ptr((HostDBInfo *)nullptr); + return NO_RECORD; } // If the record is stale, but we want to revalidate-- lets start that up - if ((!ignore_timeout && r->is_ip_stale() && !r->reverse_dns) || (r->is_ip_timeout() && r->serve_stale_but_revalidate())) { + if ((!ignore_timeout && record->is_ip_stale() && record->record_type != HostDBType::HOST) || + (record->is_ip_timeout() && record->serve_stale_but_revalidate())) { if (hostDB.is_pending_dns_for_hash(hash.hash)) { - Debug("hostdb", "stale %u %u %u, using it and pending to refresh it", r->ip_interval(), r->ip_timestamp, - r->ip_timeout_interval); - return r; - } - Debug("hostdb", "stale %u %u %u, using it and refreshing it", r->ip_interval(), r->ip_timestamp, r->ip_timeout_interval); + Debug("hostdb", "%s", + ts::bwprint(ts::bw_dbg, "stale {} {} {}, using with pending refresh", record->ip_interval(), + record->ip_timestamp.time_since_epoch(), record->ip_timeout_interval) + .c_str()); + return record; + } + Debug("hostdb", "%s", + ts::bwprint(ts::bw_dbg, "stale {} {} {}, using while refresh", record->ip_interval(), + record->ip_timestamp.time_since_epoch(), record->ip_timeout_interval) + .c_str()); HostDBContinuation *c = hostDBContAllocator.alloc(); HostDBContinuation::Options copt; - copt.host_res_style = host_res_style_for(r->ip()); + copt.host_res_style = record->af_family == AF_INET6 ? HOST_RES_IPV6_ONLY : HOST_RES_IPV4_ONLY; c->init(hash, copt); c->do_dns(); } - return r; -} - -// -// Insert a HostDBInfo into the database -// A null value indicates that the block is empty. -// -HostDBInfo * -HostDBContinuation::insert(unsigned int attl) -{ - uint64_t folded_hash = hash.hash.fold(); - - ink_assert(this_ethread() == hostDB.refcountcache->lock_for_key(folded_hash)->thread_holding); - - HostDBInfo *r = HostDBInfo::alloc(); - r->key = folded_hash; - - r->ip_timestamp = hostdb_current_interval; - r->ip_timeout_interval = std::clamp(attl, 1u, HOST_DB_MAX_TTL); - - Debug("hostdb", "inserting for: %.*s: (hash: %" PRIx64 ") now: %u timeout: %u ttl: %u", hash.host_len, hash.host_name, - folded_hash, r->ip_timestamp, r->ip_timeout_interval, attl); - - hostDB.refcountcache->put(folded_hash, r, 0, r->expiry_time()); - return r; + return record; } // @@ -658,7 +762,7 @@ HostDBProcessor::getby(Continuation *cont, cb_process_result_pfn cb_process_resu MUTEX_TRY_LOCK(lock2, bucket_mutex, thread); if (lock2.is_locked()) { // If we can get the lock and a level 1 probe succeeds, return - Ptr r = probe(bucket_mutex, hash, false); + HostDBRecord::Handle r = probe(bucket_mutex, hash, false); if (r) { // fail, see if we should retry with alternate if (hash.db_mark != HOSTDB_MARK_SRV && r->is_failed() && hash.host_name) { @@ -667,10 +771,10 @@ HostDBProcessor::getby(Continuation *cont, cb_process_result_pfn cb_process_resu if (!loop) { // No retry -> final result. Return it. if (hash.db_mark == HOSTDB_MARK_SRV) { - Debug("hostdb", "immediate SRV answer for %.*s from hostdb", hash.host_len, hash.host_name); - Debug("dns_srv", "immediate SRV answer for %.*s from hostdb", hash.host_len, hash.host_name); + Debug("hostdb", "immediate SRV answer for %.*s from hostdb", int(hash.host_name.size()), hash.host_name.data()); + Debug("dns_srv", "immediate SRV answer for %.*s from hostdb", int(hash.host_name.size()), hash.host_name.data()); } else if (hash.host_name) { - Debug("hostdb", "immediate answer for %.*s", hash.host_len, hash.host_name); + Debug("hostdb", "immediate answer for %.*s", int(hash.host_name.size()), hash.host_name.data()); } else { Debug("hostdb", "immediate answer for %s", hash.ip.isValid() ? hash.ip.toString(ipb, sizeof ipb) : ""); } @@ -688,12 +792,13 @@ HostDBProcessor::getby(Continuation *cont, cb_process_result_pfn cb_process_resu } } if (hash.db_mark == HOSTDB_MARK_SRV) { - Debug("hostdb", "delaying (force=%d) SRV answer for %.*s [timeout = %d]", force_dns, hash.host_len, hash.host_name, - opt.timeout); - Debug("dns_srv", "delaying (force=%d) SRV answer for %.*s [timeout = %d]", force_dns, hash.host_len, hash.host_name, - opt.timeout); + Debug("hostdb", "delaying (force=%d) SRV answer for %.*s [timeout = %d]", force_dns, int(hash.host_name.size()), + hash.host_name.data(), opt.timeout); + Debug("dns_srv", "delaying (force=%d) SRV answer for %.*s [timeout = %d]", force_dns, int(hash.host_name.size()), + hash.host_name.data(), opt.timeout); } else if (hash.host_name) { - Debug("hostdb", "delaying (force=%d) answer for %.*s [timeout %d]", force_dns, hash.host_len, hash.host_name, opt.timeout); + Debug("hostdb", "delaying (force=%d) answer for %.*s [timeout %d]", force_dns, int(hash.host_name.size()), + hash.host_name.data(), opt.timeout); } else { Debug("hostdb", "delaying (force=%d) answer for %s [timeout %d]", force_dns, hash.ip.isValid() ? hash.ip.toString(ipb, sizeof ipb) : "", opt.timeout); @@ -726,7 +831,7 @@ HostDBProcessor::getbyname_re(Continuation *cont, const char *ahostname, int len ink_assert(nullptr != ahostname); // Load the hash data. - hash.set_host(ahostname, ahostname ? (len ? len : strlen(ahostname)) : 0); + hash.set_host({ahostname, ahostname ? (len ? len : strlen(ahostname)) : 0}); // Leave hash.ip invalid hash.port = 0; hash.db_mark = db_mark_for(opt.host_res_style); @@ -743,7 +848,7 @@ HostDBProcessor::getbynameport_re(Continuation *cont, const char *ahostname, int ink_assert(nullptr != ahostname); // Load the hash data. - hash.set_host(ahostname, ahostname ? (len ? len : strlen(ahostname)) : 0); + hash.set_host({ahostname, ahostname ? (len ? len : strlen(ahostname)) : 0}); // Leave hash.ip invalid hash.port = opt.port; hash.db_mark = db_mark_for(opt.host_res_style); @@ -782,7 +887,7 @@ HostDBProcessor::getSRVbyname_imm(Continuation *cont, cb_process_result_pfn proc ink_assert(nullptr != hostname); - hash.set_host(hostname, len ? len : strlen(hostname)); + hash.set_host({hostname, len ? len : strlen(hostname)}); // Leave hash.ip invalid hash.port = 0; hash.db_mark = HOSTDB_MARK_SRV; @@ -802,7 +907,7 @@ HostDBProcessor::getbyname_imm(Continuation *cont, cb_process_result_pfn process ink_assert(nullptr != hostname); - hash.set_host(hostname, len ? len : strlen(hostname)); + hash.set_host({hostname, len ? len : strlen(hostname)}); // Leave hash.ip invalid // TODO: May I rename the wrapper name to getbynameport_imm ? - oknet // By comparing getbyname_re and getbynameport_re, the hash.port should be 0 if only get hostinfo by name. @@ -837,150 +942,35 @@ HostDBProcessor::iterate(Continuation *cont) return &c->action; } -static void -do_setby(HostDBInfo *r, HostDBApplicationInfo *app, const char *hostname, IpAddr const &ip, bool is_srv = false) -{ - HostDBRoundRobin *rr = r->rr(); - - if (is_srv && (!r->is_srv || !rr)) { - return; - } - - if (rr) { - if (is_srv) { - uint32_t key = makeHostHash(hostname); - for (int i = 0; i < rr->rrcount; i++) { - if (key == rr->info(i).data.srv.key && !strcmp(hostname, rr->info(i).srvname(rr))) { - Debug("hostdb", "immediate setby for %s", hostname); - rr->info(i).app.allotment.application1 = app->allotment.application1; - rr->info(i).app.allotment.application2 = app->allotment.application2; - return; - } - } - } else { - for (int i = 0; i < rr->rrcount; i++) { - if (rr->info(i).ip() == ip) { - Debug("hostdb", "immediate setby for %s", hostname ? hostname : ""); - rr->info(i).app.allotment.application1 = app->allotment.application1; - rr->info(i).app.allotment.application2 = app->allotment.application2; - return; - } - } - } - } else { - if (r->reverse_dns || (!r->round_robin && ip == r->ip())) { - Debug("hostdb", "immediate setby for %s", hostname ? hostname : ""); - r->app.allotment.application1 = app->allotment.application1; - r->app.allotment.application2 = app->allotment.application2; - } - } -} - -void -HostDBProcessor::setby(const char *hostname, int len, sockaddr const *ip, HostDBApplicationInfo *app) -{ - if (!hostdb_enable) { - return; - } - - HostDBHash hash; - hash.set_host(hostname, hostname ? (len ? len : strlen(hostname)) : 0); - hash.ip.assign(ip); - hash.port = ip ? ats_ip_port_host_order(ip) : 0; - hash.db_mark = db_mark_for(ip); - hash.refresh(); - - // Attempt to find the result in-line, for level 1 hits - - Ptr mutex = hostDB.refcountcache->lock_for_key(hash.hash.fold()); - EThread *thread = this_ethread(); - MUTEX_TRY_LOCK(lock, mutex, thread); - - if (lock.is_locked()) { - Ptr r = probe(mutex, hash, false); - if (r) { - do_setby(r.get(), app, hostname, hash.ip); - } - return; - } - // Create a continuation to do a deeper probe in the background - - HostDBContinuation *c = hostDBContAllocator.alloc(); - c->init(hash); - c->app.allotment.application1 = app->allotment.application1; - c->app.allotment.application2 = app->allotment.application2; - SET_CONTINUATION_HANDLER(c, (HostDBContHandler)&HostDBContinuation::setbyEvent); - thread->schedule_in(c, MUTEX_RETRY_DELAY); -} - -void -HostDBProcessor::setby_srv(const char *hostname, int len, const char *target, HostDBApplicationInfo *app) -{ - if (!hostdb_enable || !hostname || !target) { - return; - } - - HostDBHash hash; - hash.set_host(hostname, len ? len : strlen(hostname)); - hash.port = 0; - hash.db_mark = HOSTDB_MARK_SRV; - hash.refresh(); - - // Create a continuation to do a deeper probe in the background - - HostDBContinuation *c = hostDBContAllocator.alloc(); - c->init(hash); - ink_strlcpy(c->srv_target_name, target, MAXDNAME); - c->app.allotment.application1 = app->allotment.application1; - c->app.allotment.application2 = app->allotment.application2; - SET_CONTINUATION_HANDLER(c, (HostDBContHandler)&HostDBContinuation::setbyEvent); - eventProcessor.schedule_imm(c); -} -int -HostDBContinuation::setbyEvent(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */) -{ - Ptr r = probe(mutex, hash, false); - - if (r) { - do_setby(r.get(), &app, hash.host_name, hash.ip, is_srv()); - } - - hostdb_cont_free(this); - return EVENT_DONE; -} - // Lookup done, insert into the local table, return data to the // calling continuation. // NOTE: if "i" exists it means we already allocated the space etc, just return // -HostDBInfo * -HostDBContinuation::lookup_done(IpAddr const &ip, const char *aname, bool around_robin, unsigned int ttl_seconds, SRVHosts *srv, - HostDBInfo *r) +Ptr +HostDBContinuation::lookup_done(TextView query_name, ts_seconds answer_ttl, SRVHosts *srv, Ptr record) { ink_assert(this_ethread() == hostDB.refcountcache->lock_for_key(hash.hash.fold())->thread_holding); - if (!ip.isValid() || !aname || !aname[0]) { + ink_assert(record); + if (query_name.empty()) { if (is_byname()) { - Debug("hostdb", "lookup_done() failed for '%.*s'", hash.host_len, hash.host_name); + Debug("hostdb", "lookup_done() failed for '%.*s'", int(hash.host_name.size()), hash.host_name.data()); } else if (is_srv()) { - Debug("dns_srv", "SRV failed for '%.*s'", hash.host_len, hash.host_name); + Debug("dns_srv", "SRV failed for '%.*s'", int(hash.host_name.size()), hash.host_name.data()); } else { ip_text_buffer b; Debug("hostdb", "failed for %s", hash.ip.toString(b, sizeof b)); } - if (r == nullptr) { - r = insert(hostdb_ip_fail_timeout_interval); - } else { - r->ip_timestamp = hostdb_current_interval; - r->ip_timeout_interval = std::clamp(hostdb_ip_fail_timeout_interval, 1u, HOST_DB_MAX_TTL); - } + record->ip_timestamp = hostdb_current_interval; + record->ip_timeout_interval = ts_seconds(std::clamp(hostdb_ip_fail_timeout_interval, 1u, HOST_DB_MAX_TTL)); - r->round_robin = false; - r->round_robin_elt = false; - r->is_srv = is_srv(); - r->reverse_dns = !is_byname() && !is_srv(); + if (is_srv()) { + record->record_type = HostDBType::SRV; + } else if (!is_byname()) { + record->record_type = HostDBType::HOST; + } - r->set_failed(); - return r; + record->set_failed(); + return record; } else { switch (hostdb_ttl_mode) { @@ -989,65 +979,38 @@ HostDBContinuation::lookup_done(IpAddr const &ip, const char *aname, bool around case TTL_OBEY: break; case TTL_IGNORE: - ttl_seconds = hostdb_ip_timeout_interval; + answer_ttl = ts_seconds(hostdb_ip_timeout_interval); break; case TTL_MIN: - if (hostdb_ip_timeout_interval < ttl_seconds) { - ttl_seconds = hostdb_ip_timeout_interval; + if (ts_seconds(hostdb_ip_timeout_interval) < answer_ttl) { + answer_ttl = ts_seconds(hostdb_ip_timeout_interval); } break; case TTL_MAX: - if (hostdb_ip_timeout_interval > ttl_seconds) { - ttl_seconds = hostdb_ip_timeout_interval; + if (ts_seconds(hostdb_ip_timeout_interval) > answer_ttl) { + answer_ttl = ts_seconds(hostdb_ip_timeout_interval); } break; } - HOSTDB_SUM_DYN_STAT(hostdb_ttl_stat, ttl_seconds); + HOSTDB_SUM_DYN_STAT(hostdb_ttl_stat, answer_ttl.count()); - if (r == nullptr) { - r = insert(ttl_seconds); - } else { - // update the TTL - r->ip_timestamp = hostdb_current_interval; - r->ip_timeout_interval = std::clamp(ttl_seconds, 1u, HOST_DB_MAX_TTL); - } + // update the TTL + record->ip_timestamp = hostdb_current_interval; + record->ip_timeout_interval = std::clamp(answer_ttl, ts_seconds(1), ts_seconds(HOST_DB_MAX_TTL)); - r->round_robin_elt = false; // only true for elements explicitly added as RR elements. if (is_byname()) { - ip_text_buffer b; - Debug("hostdb", "done %s TTL %d", ip.toString(b, sizeof b), ttl_seconds); - ats_ip_set(r->ip(), ip); - r->round_robin = around_robin; - r->reverse_dns = false; - if (hash.host_name != aname) { - ink_strlcpy(hash_host_name_store, aname, sizeof(hash_host_name_store)); - } - r->is_srv = false; + Debug("hostdb", "done %.*s TTL %ld", int(hash.host_name.size()), hash.host_name.data(), answer_ttl.count()); } else if (is_srv()) { - ink_assert(srv && srv->hosts.size() && srv->hosts.size() <= hostdb_round_robin_max_count && around_robin); - - r->data.srv.srv_offset = srv->hosts.size(); - r->reverse_dns = false; - r->is_srv = true; - r->round_robin = around_robin; - - if (hash.host_name != aname) { - ink_strlcpy(hash_host_name_store, aname, sizeof(hash_host_name_store)); - } + ink_assert(srv && srv->hosts.size() && srv->hosts.size() <= hostdb_round_robin_max_count); + record->record_type = HostDBType::SRV; } else { - Debug("hostdb", "done '%s' TTL %d", aname, ttl_seconds); - // TODO: check that this is right, it seems that the 2 hostnames are always the same - r->data.hostname_offset = r->hostname_offset; - // TODO: consolidate into a single "item type" field? - r->round_robin = false; - r->reverse_dns = true; - r->is_srv = false; + Debug("hostdb", "done '%.*s' TTL %ld", int(query_name.size()), query_name.data(), answer_ttl.count()); + record->record_type = HostDBType::HOST; } } - ink_assert(!r->round_robin || !r->reverse_dns); - return r; + return record; } int @@ -1077,28 +1040,7 @@ HostDBContinuation::dnsPendingEvent(int event, Event *e) } } -// for a new HostDBInfo `r`, "inherit" from the old version of yourself if it exists in `old_rr_data` -static int -restore_info(HostDBInfo *r, HostDBInfo *old_r, HostDBInfo &old_info, HostDBRoundRobin *old_rr_data) -{ - if (old_rr_data) { - for (int j = 0; j < old_rr_data->rrcount; j++) { - if (ats_ip_addr_eq(old_rr_data->info(j).ip(), r->ip())) { - r->app = old_rr_data->info(j).app; - return true; - } - } - } else if (old_r) { - if (ats_ip_addr_eq(old_info.ip(), r->ip())) { - r->app = old_info.app; - return true; - } - } - return false; -} - // DNS lookup result state -// int HostDBContinuation::dnsEvent(int event, HostEnt *e) { @@ -1134,38 +1076,25 @@ HostDBContinuation::dnsEvent(int event, HostEnt *e) } else { bool failed = !e || !e->good; - bool is_rr = false; pending_action = nullptr; - if (is_srv()) { - is_rr = !failed && (e->srv_hosts.hosts.size() > 0); - } else if (!failed) { - is_rr = nullptr != e->ent.h_addr_list[1]; - } else { - } - - ttl = failed ? 0 : e->ttl / 60; - int ttl_seconds = failed ? 0 : e->ttl; // ebalsa: moving to second accuracy + ttl = ts_seconds(failed ? 0 : e->ttl); - Ptr old_r = probe(mutex, hash, false); + Ptr old_r = probe(mutex, hash, false); // If the DNS lookup failed with NXDOMAIN, remove the old record if (e && e->isNameError() && old_r) { hostDB.refcountcache->erase(old_r->key); old_r = nullptr; Debug("hostdb", "Removing the old record when the DNS lookup failed with NXDOMAIN"); } - HostDBInfo old_info; - if (old_r) { - old_info = *old_r.get(); - } - HostDBRoundRobin *old_rr_data = old_r ? old_r->rr() : nullptr; - int valid_records = 0; - void *first_record = nullptr; - uint8_t af = e ? e->ent.h_addrtype : AF_UNSPEC; // address family - // if this is an RR response, we need to find the first record, as well as the - // total number of records - if (is_rr) { - if (is_srv() && !failed) { + + int valid_records = 0; + void *first_record = nullptr; + sa_family_t af = e ? e->ent.h_addrtype : AF_UNSPEC; // address family + + // Find the first record and total number of records. + if (!failed) { + if (is_srv()) { valid_records = e->srv_hosts.hosts.size(); } else { void *ptr; // tmp for current entry. @@ -1185,160 +1114,91 @@ HostDBContinuation::dnsEvent(int event, HostEnt *e) ++valid_records; } else { - Warning("Zero address removed from round-robin list for '%s'", hash.host_name); + Warning("Invalid address removed for '%.*s'", int(hash.host_name.size()), hash.host_name.data()); } } if (!first_record) { failed = true; - is_rr = false; } } - } else if (!failed) { - first_record = e->ent.h_addr_list[0]; - } // else first is 0. - - IpAddr tip; // temp storage if needed. + } // else first is nullptr // In the event that the lookup failed (SOA response-- for example) we want to use hash.host_name, since it'll be "" - const char *aname = (failed || strlen(hash.host_name)) ? hash.host_name : e->ent.h_name; - - const size_t s_size = strlen(aname) + 1; - const size_t rrsize = is_rr ? HostDBRoundRobin::size(valid_records, e->srv_hosts.srv_hosts_length) : 0; - // where in our block of memory we are - int offset = sizeof(HostDBInfo); - - int allocSize = s_size + rrsize; // The extra space we need for the rest of the things - - HostDBInfo *r = HostDBInfo::alloc(allocSize); - Debug("hostdb", "allocating %d bytes for %s with %d RR records at [%p]", allocSize, aname, valid_records, r); - // set up the record - r->key = hash.hash.fold(); // always set the key - - r->hostname_offset = offset; - ink_strlcpy(r->perm_hostname(), aname, s_size); - offset += s_size; + TextView query_name = (failed || !hash.host_name.empty()) ? hash.host_name : TextView{e->ent.h_name, strlen(e->ent.h_name)}; + HostDBRecord::Handle r{HostDBRecord::alloc(query_name, valid_records, failed ? 0 : e->srv_hosts.srv_hosts_length)}; + r->key = hash.hash.fold(); // always set the key + r->af_family = af; + r->flags.f.failed_p = failed; // If the DNS lookup failed (errors such as SERVFAIL, etc.) but we have an old record // which is okay with being served stale-- lets continue to serve the stale record as long as // the record is willing to be served. bool serve_stale = false; if (failed && old_r && old_r->serve_stale_but_revalidate()) { - r->free(); - r = old_r.get(); + r = old_r; serve_stale = true; + // Should return here? No point in doing initialization, it's the old data. } else if (is_byname()) { - if (first_record) { - ip_addr_set(tip, af, first_record); - } - r = lookup_done(tip, hash.host_name, is_rr, ttl_seconds, failed ? nullptr : &e->srv_hosts, r); + lookup_done(hash.host_name, ttl, failed ? nullptr : &e->srv_hosts, r); } else if (is_srv()) { - if (!failed) { - tip._family = AF_INET; // force the tip valid, or else the srv will fail - } - r = lookup_done(tip, /* junk: FIXME: is the code in lookup_done() wrong to NEED this? */ - hash.host_name, /* hostname */ - is_rr, /* is round robin, doesnt matter for SRV since we recheck getCount() inside lookup_done() */ - ttl_seconds, /* ttl in seconds */ - failed ? nullptr : &e->srv_hosts, r); + lookup_done(hash.host_name, /* hostname */ + ttl, /* ttl in seconds */ + failed ? nullptr : &e->srv_hosts, r); } else if (failed) { - r = lookup_done(tip, hash.host_name, false, ttl_seconds, nullptr, r); + lookup_done(hash.host_name, ttl, nullptr, r); } else { - r = lookup_done(hash.ip, e->ent.h_name, false, ttl_seconds, &e->srv_hosts, r); + lookup_done(e->ent.h_name, ttl, &e->srv_hosts, r); } - // Conditionally make rr record entries - if (is_rr) { - r->app.rr.offset = offset; - // This will only be set if is_rr - HostDBRoundRobin *rr_data = static_cast(r->rr()); - ; - if (is_srv()) { - int skip = 0; - char *pos = reinterpret_cast(rr_data) + sizeof(HostDBRoundRobin) + valid_records * sizeof(HostDBInfo); - SRV *q[valid_records]; - ink_assert(valid_records <= (int)hostdb_round_robin_max_count); - // sort - for (int i = 0; i < valid_records; ++i) { - q[i] = &e->srv_hosts.hosts[i]; - } - for (int i = 0; i < valid_records; ++i) { - for (int ii = i + 1; ii < valid_records; ++ii) { - if (*q[ii] < *q[i]) { - SRV *tmp = q[i]; - q[i] = q[ii]; - q[ii] = tmp; + auto rr_info = r->rr_info(); + // Fill in record type specific data. + if (is_srv()) { + char *pos = rr_info.rebind().end(); + SRV *q[valid_records]; + ink_assert(valid_records <= (int)hostdb_round_robin_max_count); + for (int i = 0; i < valid_records; ++i) { + q[i] = &e->srv_hosts.hosts[i]; + } + std::sort(q, q + valid_records, [](SRV *lhs, SRV *rhs) -> bool { return *lhs < *rhs; }); + + SRV **cur_srv = q; + for (auto &item : rr_info) { + auto t = *cur_srv++; // get next SRV record pointer. + memcpy(pos, t->host, t->host_len); // Append the name to the overall record. + item.assign(t, pos); + pos += t->host_len; + if (old_r) { // migrate as needed. + for (auto &old_item : old_r->rr_info()) { + if (item.data.srv.key == old_item.data.srv.key && 0 == strcmp(item.srvname(), old_item.srvname())) { + item.migrate_from(old_item); + break; } } } - rr_data->good = rr_data->rrcount = valid_records; - rr_data->current = 0; - for (int i = 0; i < valid_records; ++i) { - SRV *t = q[i]; - HostDBInfo &item = rr_data->info(i); - item.round_robin = 0; - item.round_robin_elt = 1; - item.reverse_dns = 0; - item.is_srv = 1; - item.data.srv.srv_weight = t->weight; - item.data.srv.srv_priority = t->priority; - item.data.srv.srv_port = t->port; - item.data.srv.key = t->key; - - ink_assert((skip + t->host_len) <= e->srv_hosts.srv_hosts_length); - - memcpy(pos + skip, t->host, t->host_len); - item.data.srv.srv_offset = (pos - reinterpret_cast(rr_data)) + skip; - - skip += t->host_len; - - item.app.allotment.application1 = 0; - item.app.allotment.application2 = 0; - Debug("dns_srv", "inserted SRV RR record [%s] into HostDB with TTL: %d seconds", t->host, ttl_seconds); - } - - // restore - if (old_rr_data) { - for (int i = 0; i < rr_data->rrcount; ++i) { - for (int ii = 0; ii < old_rr_data->rrcount; ++ii) { - if (rr_data->info(i).data.srv.key == old_rr_data->info(ii).data.srv.key) { - char *new_host = rr_data->info(i).srvname(rr_data); - char *old_host = old_rr_data->info(ii).srvname(old_rr_data); - if (!strcmp(new_host, old_host)) { - rr_data->info(i).app = old_rr_data->info(ii).app; - } - } + Debug("dns_srv", "inserted SRV RR record [%s] into HostDB with TTL: %ld seconds", t->host, ttl.count()); + } + } else { // Otherwise this is a regular dns response + unsigned idx = 0; + for (auto &item : rr_info) { + item.assign(af, e->ent.h_addr_list[idx++]); + if (old_r) { // migrate as needed. + for (auto &old_item : old_r->rr_info()) { + if (item.data.ip == old_item.data.ip) { + item.migrate_from(old_item); + break; } } } - } else { // Otherwise this is a regular dns response - rr_data->good = rr_data->rrcount = valid_records; - rr_data->current = 0; - for (int i = 0; i < valid_records; ++i) { - HostDBInfo &item = rr_data->info(i); - ip_addr_set(item.ip(), af, e->ent.h_addr_list[i]); - item.round_robin = 0; - item.round_robin_elt = 1; - item.reverse_dns = 0; - item.is_srv = 0; - if (!restore_info(&item, old_r.get(), old_info, old_rr_data)) { - item.app.allotment.application1 = 0; - item.app.allotment.application2 = 0; - } - } } } - if (!failed && !is_rr && !is_srv()) { - restore_info(r, old_r.get(), old_info, old_rr_data); - } - ink_assert(!r || !r->round_robin || !r->reverse_dns); - ink_assert(failed || !r->round_robin || r->app.rr.offset); - if (!serve_stale) { - hostDB.refcountcache->put(hash.hash.fold(), r, allocSize, r->expiry_time()); + hostDB.refcountcache->put( + r->key, r.get(), r->_record_size, + (r->ip_timestamp + r->ip_timeout_interval + ts_seconds(hostdb_serve_stale_but_revalidate)).time_since_epoch().count()); } else { - Warning("Fallback to serving stale record, skip re-update of hostdb for %s", aname); + Warning("Fallback to serving stale record, skip re-update of hostdb for %.*s", int(query_name.size()), query_name.data()); } // try to callback the user @@ -1363,7 +1223,7 @@ HostDBContinuation::dnsEvent(int event, HostEnt *e) if (action.continuation->mutex) { ink_release_assert(action.continuation->mutex == action.mutex); } - reply_to_cont(action.continuation, r, is_srv()); + reply_to_cont(action.continuation, r.get(), is_srv()); } need_to_reschedule = false; } @@ -1423,7 +1283,7 @@ HostDBContinuation::iterateEvent(int event, Event *e) IntrusiveHashMap &partMap = hostDB.refcountcache->get_partition(current_iterate_pos).get_map(); for (const auto &it : partMap) { - HostDBInfo *r = static_cast(it.item.get()); + auto *r = static_cast(it.item.get()); if (r && !r->is_failed()) { action.continuation->handleEvent(EVENT_INTERVAL, static_cast(r)); } @@ -1489,7 +1349,7 @@ HostDBContinuation::probeEvent(int /* event ATS_UNUSED */, Event *e) if (!force_dns) { // Do the probe // - Ptr r = probe(mutex, hash, false); + Ptr r = probe(mutex, hash, false); if (r) { HOSTDB_INCREMENT_DYN_STAT(hostdb_total_hits_stat); @@ -1573,56 +1433,67 @@ HostDBContinuation::do_dns() { ink_assert(!action.cancelled); if (is_byname()) { - Debug("hostdb", "DNS %s", hash.host_name); + Debug("hostdb", "DNS %.*s", int(hash.host_name.size()), hash.host_name.data()); IpAddr tip; if (0 == tip.load(hash.host_name)) { - // check 127.0.0.1 format // What the heck does that mean? - AMC + // Need to consider if this is necessary - could the record in ResolveInfo be left null and + // just the resolved address set? if (action.continuation) { - HostDBInfo *r = lookup_done(tip, hash.host_name, false, HOST_DB_MAX_TTL, nullptr); - - reply_to_cont(action.continuation, r); + HostDBRecord::Handle r{HostDBRecord::alloc(hash.host_name, 1)}; + r->af_family = tip.family(); + auto &info = r->rr_info()[0]; + info.assign(tip); + // tricksy - @a reply_to_cont must use an intrusive pointer to @a r if it needs to persist + // @a r doesn't go out of scope until after this returns. This continuation shares the mutex + // of the target continuation therefore this is always dispatched synchronously. + reply_to_cont(action.continuation, r.get()); } hostdb_cont_free(this); return; } - ts::ConstBuffer hname(hash.host_name, hash.host_len); - Ptr current_host_file_map = hostDB.hosts_file_ptr; - HostsFileMap::iterator find_result = current_host_file_map->hosts_file_map.find(hname); - if (find_result != current_host_file_map->hosts_file_map.end()) { - if (action.continuation) { - // Set the TTL based on how often we stat() the host file - HostDBInfo *r = lookup_done(IpAddr(find_result->second), hash.host_name, false, hostdb_hostfile_check_interval, nullptr); - reply_to_cont(action.continuation, r); + + // If looking for an IPv4 or IPv6 address, check the host file. + if (hash.db_mark == HOSTDB_MARK_IPV6 || hash.db_mark == HOSTDB_MARK_IPV4) { + if (auto static_hosts = hostDB.acquire_host_file(); static_hosts) { + if (auto spot = static_hosts->find(hash.host_name); spot != static_hosts->end()) { + HostDBRecord::Handle r = (hash.db_mark == HOSTDB_MARK_IPV4) ? spot->second.record_4 : spot->second.record_6; + // Set the TTL based on how often we stat() the host file + if (r && action.continuation) { + r = lookup_done(hash.host_name, hostdb_hostfile_check_interval, nullptr, r); + reply_to_cont(action.continuation, r.get()); + hostdb_cont_free(this); + return; + } + } } - hostdb_cont_free(this); - return; } - } - if (hostdb_lookup_timeout) { - timeout = mutex->thread_holding->schedule_in(this, HRTIME_SECONDS(hostdb_lookup_timeout)); - } else { - timeout = nullptr; - } - if (set_check_pending_dns()) { - DNSProcessor::Options opt; - opt.timeout = dns_lookup_timeout; - opt.host_res_style = host_res_style_for(hash.db_mark); - SET_HANDLER((HostDBContHandler)&HostDBContinuation::dnsEvent); - if (is_byname()) { - if (hash.dns_server) { - opt.handler = hash.dns_server->x_dnsH; + + if (hostdb_lookup_timeout) { + timeout = mutex->thread_holding->schedule_in(this, HRTIME_SECONDS(hostdb_lookup_timeout)); + } else { + timeout = nullptr; + } + if (set_check_pending_dns()) { + DNSProcessor::Options opt; + opt.timeout = dns_lookup_timeout; + opt.host_res_style = host_res_style_for(hash.db_mark); + SET_HANDLER((HostDBContHandler)&HostDBContinuation::dnsEvent); + if (is_byname()) { + if (hash.dns_server) { + opt.handler = hash.dns_server->x_dnsH; + } + pending_action = dnsProcessor.gethostbyname(this, hash.host_name, opt); + } else if (is_srv()) { + Debug("dns_srv", "SRV lookup of %.*s", int(hash.host_name.size()), hash.host_name.data()); + pending_action = dnsProcessor.getSRVbyname(this, hash.host_name, opt); + } else { + ip_text_buffer ipb; + Debug("hostdb", "DNS IP %s", hash.ip.toString(ipb, sizeof ipb)); + pending_action = dnsProcessor.gethostbyaddr(this, &hash.ip, opt); } - pending_action = dnsProcessor.gethostbyname(this, hash.host_name, opt); - } else if (is_srv()) { - Debug("dns_srv", "SRV lookup of %s", hash.host_name); - pending_action = dnsProcessor.getSRVbyname(this, hash.host_name, opt); } else { - ip_text_buffer ipb; - Debug("hostdb", "DNS IP %s", hash.ip.toString(ipb, sizeof ipb)); - pending_action = dnsProcessor.gethostbyaddr(this, &hash.ip, opt); + SET_HANDLER((HostDBContHandler)&HostDBContinuation::dnsPendingEvent); } - } else { - SET_HANDLER((HostDBContHandler)&HostDBContinuation::dnsPendingEvent); } } @@ -1634,44 +1505,41 @@ HostDBContinuation::do_dns() int HostDBContinuation::backgroundEvent(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */) { + std::string dbg; + // No nothing if hosts file checking is not enabled. - if (hostdb_hostfile_check_interval == 0) { + if (hostdb_hostfile_check_interval.count() == 0) { return EVENT_CONT; } - hostdb_current_interval = ink_time(); + hostdb_current_interval = ts_clock::now(); if ((hostdb_current_interval - hostdb_last_interval) > hostdb_hostfile_check_interval) { bool update_p = false; // do we need to reparse the file and update? - struct stat info; - char path[sizeof(hostdb_hostfile_path)]; + char path[PATH_NAME_MAX]; REC_ReadConfigString(path, "proxy.config.hostdb.host_file.path", sizeof(path)); - if (0 != strcasecmp(hostdb_hostfile_path, path)) { - Debug("hostdb", "Update host file '%s' -> '%s'", (*hostdb_hostfile_path ? hostdb_hostfile_path : "*-none-*"), - (*path ? path : "*-none-*")); + if (0 != strcasecmp(hostdb_hostfile_path.string(), path)) { + Debug("hostdb", "%s", + ts::bwprint(dbg, R"(Updating hosts file from "{}" to "{}")", hostdb_hostfile_path, ts::bwf::FirstOf(path, "")).c_str()); // path to hostfile changed - hostdb_hostfile_update_timestamp = 0; // never updated from this file - if ('\0' != *path) { - memcpy(hostdb_hostfile_path, path, sizeof(hostdb_hostfile_path)); - } else { - hostdb_hostfile_path[0] = 0; // mark as not there - } - update_p = true; - } else { + hostdb_hostfile_update_timestamp = TS_TIME_ZERO; // never updated from this file + hostdb_hostfile_path = path; + update_p = true; + } else if (!hostdb_hostfile_path.empty()) { hostdb_last_interval = hostdb_current_interval; - if (*hostdb_hostfile_path) { - if (0 == stat(hostdb_hostfile_path, &info)) { - if (info.st_mtime > static_cast(hostdb_hostfile_update_timestamp)) { - update_p = true; // same file but it's changed. - } - } else { - Debug("hostdb", "Failed to stat host file '%s'", hostdb_hostfile_path); + std::error_code ec; + auto stat{ts::file::status(hostdb_hostfile_path, ec)}; + if (!ec) { + if (ts_clock::from_time_t(modification_time(stat)) > hostdb_hostfile_update_timestamp) { + update_p = true; // same file but it's changed. } + } else { + Debug("hostdb", "%s", ts::bwprint(dbg, R"(Failed to stat host file "{}" - {})", hostdb_hostfile_path, ec).c_str()); } } if (update_p) { - Debug("hostdb", "Updating from host file"); + Debug("hostdb", "%s", ts::bwprint(dbg, R"(Updating from host file "{}")", hostdb_hostfile_path).c_str()); ParseHostFile(hostdb_hostfile_path, hostdb_hostfile_check_interval); } } @@ -1679,37 +1547,60 @@ HostDBContinuation::backgroundEvent(int /* event ATS_UNUSED */, Event * /* e ATS return EVENT_CONT; } -char * -HostDBInfo::hostname() const -{ - if (!reverse_dns) { - return nullptr; - } - - return (char *)this + data.hostname_offset; -} - -/* - * The perm_hostname exists for all records not just reverse dns records. - */ -char * -HostDBInfo::perm_hostname() const +HostDBInfo * +HostDBRecord::select_best_http(ts_time now, ts_seconds fail_window, sockaddr const *hash_addr) { - if (hostname_offset == 0) { - return nullptr; + ink_assert(0 < rr_count && rr_count <= hostdb_round_robin_max_count); + + // @a best_any is set to a base candidate, which may be dead. + HostDBInfo *best_any = nullptr; + // @a best_alive is set when a valid target has been selected and should be used. + HostDBInfo *best_alive = nullptr; + + auto info{this->rr_info()}; + + if (HostDBProcessor::hostdb_strict_round_robin) { + // Always select the next viable target - select failure means no valid targets at all. + best_alive = best_any = this->select_next_rr(now, fail_window); + Debug("hostdb", "Using strict round robin - index %d", this->index_of(best_alive)); + } else if (HostDBProcessor::hostdb_timed_round_robin > 0) { + auto ctime = rr_ctime.load(); // cache for atomic update. + auto ntime = ctime + ts_seconds(HostDBProcessor::hostdb_timed_round_robin); + // Check and update RR if it's time - this always yields a valid target if there is one. + if (now > ntime && rr_ctime.compare_exchange_strong(ctime, ntime)) { + best_alive = best_any = this->select_next_rr(now, fail_window); + Debug("hostdb", "Round robin timed interval expired - index %d", this->index_of(best_alive)); + } else { // pick the current index, which may be dead. + best_any = &info[this->rr_idx()]; + } + Debug("hostdb", "Using timed round robin - index %d", this->index_of(best_any)); + } else { + // Walk the entries and find the best (largest) hash. + unsigned int best_hash = 0; // any hash is better than this. + for (auto &target : info) { + unsigned int h = HOSTDB_CLIENT_IP_HASH(hash_addr, target.data.ip); + if (best_hash <= h) { + best_any = ⌖ + best_hash = h; + } + } + Debug("hostdb", "Using client affinity - index %d", this->index_of(best_any)); } - return (char *)this + hostname_offset; -} - -HostDBRoundRobin * -HostDBInfo::rr() -{ - if (!round_robin) { - return nullptr; + // If there is a base choice, search for valid target starting there. + // Otherwise there is no valid target in the record. + if (best_any && !best_alive) { + // Starting at the current target, search for a valid one. + for (unsigned short i = 0; i < rr_count; i++) { + auto target = &info[this->rr_idx(i)]; + if (target->select(now, fail_window)) { + best_alive = target; + break; + } + } } - return reinterpret_cast(reinterpret_cast(this) + this->app.rr.offset); + return best_alive; } struct ShowHostDB; @@ -1775,41 +1666,42 @@ struct ShowHostDB : public ShowCont { showAllEvent(int event, Event *e) { if (event == EVENT_INTERVAL) { - HostDBInfo *r = reinterpret_cast(e); + auto *r = reinterpret_cast(e); if (output_json && records_seen++ > 0) { CHECK_SHOW(show(",")); // we need to separate records } - showOne(r, false, event, e); - if (r->round_robin) { - HostDBRoundRobin *rr_data = r->rr(); - if (rr_data) { - if (!output_json) { - CHECK_SHOW(show("\n")); - CHECK_SHOW(show("\n", "Total", rr_data->rrcount)); - CHECK_SHOW(show("\n", "Good", rr_data->good)); - CHECK_SHOW(show("\n", "Current", rr_data->current)); - CHECK_SHOW(show("
%s%d
%s%d
%s%d
\n")); - } else { - CHECK_SHOW(show(",\"%s\":\"%d\",", "rr_total", rr_data->rrcount)); - CHECK_SHOW(show("\"%s\":\"%d\",", "rr_good", rr_data->good)); - CHECK_SHOW(show("\"%s\":\"%d\",", "rr_current", rr_data->current)); - CHECK_SHOW(show("\"rr_records\":[")); - } + auto rr_info{r->rr_info()}; + if (rr_info.count()) { + if (!output_json) { + CHECK_SHOW(show("\n")); + CHECK_SHOW(show("\n", "Total", r->rr_count)); + CHECK_SHOW(show("\n", "Current", r->_rr_idx.load())); + CHECK_SHOW(show("\n", "Stale", r->is_ip_stale() ? "Yes" : "No")); + CHECK_SHOW(show("\n", "Timed-Out", r->is_ip_timeout() ? "Yes" : "No")); + CHECK_SHOW(show("
%s%d
%s%d
%s%s
%s%s
\n")); + } else { + CHECK_SHOW(show(",\"%s\":\"%d\",", "rr_total", r->rr_count)); + CHECK_SHOW(show("\"%s\":\"%d\",", "rr_current", r->_rr_idx.load())); + CHECK_SHOW(show("\"rr_records\":[")); + } + CHECK_SHOW(show("%s%d\n", "TTL", r->ip_time_remaining())); - for (int i = 0; i < rr_data->rrcount; i++) { - showOne(&rr_data->info(i), true, event, e, rr_data); - if (output_json) { - CHECK_SHOW(show("}")); // we need to separate records - if (i < (rr_data->rrcount - 1)) - CHECK_SHOW(show(",")); + bool need_separator = false; + for (auto &item : rr_info) { + showOne(&item, r->record_type, event, e); + if (output_json) { + CHECK_SHOW(show("}")); // we need to separate records + if (need_separator) { + CHECK_SHOW(show(",")); } + need_separator = true; } + } - if (!output_json) { - CHECK_SHOW(show("
\n
\n")); - } else { - CHECK_SHOW(show("]")); - } + if (!output_json) { + CHECK_SHOW(show("
\n
\n")); + } else { + CHECK_SHOW(show("]")); } } @@ -1831,67 +1723,47 @@ struct ShowHostDB : public ShowCont { } int - showOne(HostDBInfo *r, bool rr, int event, Event *e, HostDBRoundRobin *hostdb_rr = nullptr) + showOne(HostDBInfo *info, HostDBType record_type, int event, Event *e) { ip_text_buffer b; if (!output_json) { CHECK_SHOW(show("\n")); - CHECK_SHOW(show("\n", "Type", r->round_robin ? "Round-Robin" : "", - r->reverse_dns ? "Reverse DNS" : "", r->is_srv ? "SRV" : "DNS")); + CHECK_SHOW(show("\n", "Type", name_of(record_type))); - if (r->perm_hostname()) { - CHECK_SHOW(show("\n", "Hostname", r->perm_hostname())); - } else if (rr && r->is_srv && hostdb_rr) { - CHECK_SHOW(show("\n", "Hostname", r->srvname(hostdb_rr))); + if (HostDBType::SRV == record_type) { + CHECK_SHOW(show("\n", "Hostname", info->srvname())); } // Let's display the hash. - CHECK_SHOW(show("\n", "App1", r->app.allotment.application1)); - CHECK_SHOW(show("\n", "App2", r->app.allotment.application2)); - CHECK_SHOW(show("\n", "LastFailure", r->app.http_data.last_failure)); - if (!rr) { - CHECK_SHOW(show("\n", "Stale", r->is_ip_stale() ? "Yes" : "No")); - CHECK_SHOW(show("\n", "Timed-Out", r->is_ip_timeout() ? "Yes" : "No")); - CHECK_SHOW(show("\n", "TTL", r->ip_time_remaining())); - } + CHECK_SHOW(show("\n", "LastFailure", info->last_failure.load().time_since_epoch().count())); - if (rr && r->is_srv) { - CHECK_SHOW(show("\n", "Weight", r->data.srv.srv_weight)); - CHECK_SHOW(show("\n", "Priority", r->data.srv.srv_priority)); - CHECK_SHOW(show("\n", "Port", r->data.srv.srv_port)); - CHECK_SHOW(show("\n", "Key", r->data.srv.key)); - } else if (!r->is_srv) { - CHECK_SHOW(show("\n", "IP", ats_ip_ntop(r->ip(), b, sizeof b))); + if (HostDBType::SRV == record_type) { + CHECK_SHOW(show("\n", "Weight", info->data.srv.srv_weight)); + CHECK_SHOW(show("\n", "Priority", info->data.srv.srv_priority)); + CHECK_SHOW(show("\n", "Port", info->data.srv.srv_port)); + CHECK_SHOW(show("\n", "Key", info->data.srv.key)); + } else { + CHECK_SHOW(show("\n", "IP", info->data.ip.toString(b, sizeof b))); } CHECK_SHOW(show("
%s%s%s %s
%s%s
%s%s
%s%s
%s%s
%s%u
%s%u
%s%u
%s%s
%s%s
%s%d
%s%u
%s%d
%s%d
%s%d
%s%x
%s%s
%s%d
%s%d
%s%d
%s%x
%s%s
\n")); } else { CHECK_SHOW(show("{")); - CHECK_SHOW(show("\"%s\":\"%s%s%s\",", "type", (r->round_robin && !r->is_srv) ? "roundrobin" : "", - r->reverse_dns ? "reversedns" : "", r->is_srv ? "srv" : "dns")); + CHECK_SHOW(show("\"%s\":\"%s\",", "type", name_of(record_type))); - if (r->perm_hostname()) { - CHECK_SHOW(show("\"%s\":\"%s\",", "hostname", r->perm_hostname())); - } else if (rr && r->is_srv && hostdb_rr) { - CHECK_SHOW(show("\"%s\":\"%s\",", "hostname", r->srvname(hostdb_rr))); + if (HostDBType::SRV == record_type) { + CHECK_SHOW(show("\"%s\":\"%s\",", "hostname", info->srvname())); } - CHECK_SHOW(show("\"%s\":\"%u\",", "app1", r->app.allotment.application1)); - CHECK_SHOW(show("\"%s\":\"%u\",", "app2", r->app.allotment.application2)); - CHECK_SHOW(show("\"%s\":\"%u\",", "lastfailure", r->app.http_data.last_failure)); - if (!rr) { - CHECK_SHOW(show("\"%s\":\"%s\",", "stale", r->is_ip_stale() ? "yes" : "no")); - CHECK_SHOW(show("\"%s\":\"%s\",", "timedout", r->is_ip_timeout() ? "yes" : "no")); - CHECK_SHOW(show("\"%s\":\"%d\",", "ttl", r->ip_time_remaining())); - } + CHECK_SHOW(show("\"%s\":\"%u\",", "lastfailure", info->last_failure.load().time_since_epoch().count())); - if (rr && r->is_srv) { - CHECK_SHOW(show("\"%s\":\"%d\",", "weight", r->data.srv.srv_weight)); - CHECK_SHOW(show("\"%s\":\"%d\",", "priority", r->data.srv.srv_priority)); - CHECK_SHOW(show("\"%s\":\"%d\",", "port", r->data.srv.srv_port)); - CHECK_SHOW(show("\"%s\":\"%x\",", "key", r->data.srv.key)); - } else if (!r->is_srv) { - CHECK_SHOW(show("\"%s\":\"%s\"", "ip", ats_ip_ntop(r->ip(), b, sizeof b))); + if (HostDBType::SRV == record_type) { + CHECK_SHOW(show("\"%s\":\"%d\",", "weight", info->data.srv.srv_weight)); + CHECK_SHOW(show("\"%s\":\"%d\",", "priority", info->data.srv.srv_priority)); + CHECK_SHOW(show("\"%s\":\"%d\",", "port", info->data.srv.srv_port)); + CHECK_SHOW(show("\"%s\":\"%x\",", "key", info->data.srv.key)); + } else { + CHECK_SHOW(show("\"%s\":\"%s\"", "ip", info->data.ip.toString(b, sizeof b))); } } return EVENT_CONT; @@ -1900,7 +1772,7 @@ struct ShowHostDB : public ShowCont { int showLookupDone(int event, Event *e) { - HostDBInfo *r = reinterpret_cast(e); + auto *r = reinterpret_cast(e); CHECK_SHOW(begin("HostDB Lookup")); if (name) { @@ -1909,19 +1781,15 @@ struct ShowHostDB : public ShowCont { CHECK_SHOW(show("

%u.%u.%u.%u

\n", PRINT_IP(ip))); } if (r) { - showOne(r, false, event, e); - if (r->round_robin) { - HostDBRoundRobin *rr_data = r->rr(); - if (rr_data) { - CHECK_SHOW(show("\n")); - CHECK_SHOW(show("\n", "Total", rr_data->rrcount)); - CHECK_SHOW(show("\n", "Good", rr_data->good)); - CHECK_SHOW(show("\n", "Current", rr_data->current)); - CHECK_SHOW(show("
%s%d
%s%d
%s%d
\n")); - - for (int i = 0; i < rr_data->rrcount; i++) { - showOne(&rr_data->info(i), true, event, e, rr_data); - } + auto rr_data = r->rr_info(); + if (rr_data.count()) { + CHECK_SHOW(show("\n")); + CHECK_SHOW(show("\n", "Total", r->rr_count)); + CHECK_SHOW(show("\n", "Current", r->_rr_idx.load())); + CHECK_SHOW(show("
%s%d
%s%d
\n")); + + for (auto &item : rr_data) { + showOne(&item, r->record_type, event, e); } } } else { @@ -2017,9 +1885,9 @@ struct HostDBTestReverse : public Continuation { mainEvent(int event, Event *e) { if (event == EVENT_HOST_DB_LOOKUP) { - HostDBInfo *i = reinterpret_cast(e); + auto *i = reinterpret_cast(e); if (i) { - rprintf(test, "HostDBTestReverse: reversed %s\n", i->hostname()); + rprintf(test, "HostDBTestReverse: reversed %s\n", i->name()); } outstanding--; } @@ -2136,93 +2004,105 @@ HostDBFileContinuation::destroy() // We can't allow more than one update to be // proceeding at a time in any case so we might as well make these // globals. -int HostDBFileUpdateActive = 0; +std::atomic HostDBFileUpdateActive{false}; + +/* Container for temporarily holding data from the host file. For each FQDN there is a vector of IPv4 + * and IPv6 addresses. These are used to generate the HostDBRecord instances that are stored persistently. + */ +using HostAddrMap = std::unordered_map, std::vector>>; static void -ParseHostLine(Ptr &map, char *l) +ParseHostLine(TextView line, HostAddrMap &map) { - Tokenizer elts(" \t"); - int n_elts = elts.Initialize(l, SHARE_TOKS); - // Elements should be the address then a list of host names. + TextView addr_text = line.take_prefix_if(&isspace); + IpAddr addr; + // Don't use RecHttpLoadIp because the address *must* be literal. - IpAddr ip; - if (n_elts > 1 && 0 == ip.load(elts[0])) { - for (int i = 1; i < n_elts; ++i) { - ts::ConstBuffer name(elts[i], strlen(elts[i])); - // If we don't have an entry already (host files only support single IPs for a given name) - if (map->hosts_file_map.find(name) == map->hosts_file_map.end()) { - map->hosts_file_map[name] = ip; - } + if (TS_SUCCESS != addr.load(addr_text)) { + return; + } + + while (!line.ltrim_if(&isspace).empty()) { + TextView name = line.take_prefix_if(&isspace); + if (addr.isIp6()) { + std::get<1>(map[name]).push_back(addr); + } else if (addr.isIp4()) { + std::get<0>(map[name]).push_back(addr); } } } void -ParseHostFile(const char *path, unsigned int hostdb_hostfile_check_interval_parse) +ParseHostFile(ts::file::path const &path, ts_seconds hostdb_hostfile_check_interval_parse) { - Ptr parsed_hosts_file_ptr; + std::shared_ptr map; + std::string dbg; // Test and set for update in progress. - if (0 != ink_atomic_swap(&HostDBFileUpdateActive, 1)) { + bool flag = false; + if (!HostDBFileUpdateActive.compare_exchange_strong(flag, true)) { Debug("hostdb", "Skipped load of host file because update already in progress"); return; } - Debug("hostdb", "Loading host file '%s'", path); - - if (*path) { - ats_scoped_fd fd(open(path, O_RDONLY)); - if (fd >= 0) { - struct stat info; - if (0 == fstat(fd, &info)) { - // +1 in case no terminating newline - int64_t size = info.st_size + 1; - - parsed_hosts_file_ptr = new RefCountedHostsFileMap; - parsed_hosts_file_ptr->HostFileText = static_cast(ats_malloc(size)); - if (parsed_hosts_file_ptr->HostFileText) { - char *base = parsed_hosts_file_ptr->HostFileText; - char *limit; - - size = read(fd, parsed_hosts_file_ptr->HostFileText, info.st_size); - limit = parsed_hosts_file_ptr->HostFileText + size; - *limit = 0; - - // We need to get a list of all name/addr pairs so that we can - // group names for round robin records. Also note that the - // pairs have pointer back in to the text storage for the file - // so we need to keep that until we're done with @a pairs. - while (base < limit) { - char *spot = strchr(base, '\n'); - - // terminate the line. - if (nullptr == spot) { - spot = limit; // no trailing EOL, grab remaining - } else { - *spot = 0; - } - - while (base < spot && isspace(*base)) { - ++base; // skip leading ws - } - if (*base != '#' && base < spot) { // non-empty non-comment line - ParseHostLine(parsed_hosts_file_ptr, base); - } - base = spot + 1; - } - - hostdb_hostfile_update_timestamp = hostdb_current_interval; + Debug("hostdb", "%s", ts::bwprint(dbg, R"(Loading host file "{}")", path).c_str()); + + if (!path.empty()) { + std::error_code ec; + std::string content = ts::file::load(path, ec); + if (!ec) { + HostAddrMap addr_map; + TextView text{content}; + while (text) { + auto line = text.take_prefix_at('\n').ltrim_if(&isspace); + if (line.empty() || '#' == *line) { + continue; } + ParseHostLine(line, addr_map); } + // @a map should be loaded with all of the data, create the records. + map.reset(new HostFileMap); + // Common loading function for creating a record from the address vector. + auto loader = [](TextView key, std::vector const &v) -> HostDBRecord::Handle { + HostDBRecord::Handle record{HostDBRecord::alloc(key, v.size())}; + record->af_family = v.front().family(); // @a v is presumed family homogenous + auto rr_info = record->rr_info(); + auto spot = v.begin(); + for (auto &item : rr_info) { + item.assign(*spot++); + } + return record; + }; + // Walk the host file map and create the corresponding records. + for (auto const &[key, value] : addr_map) { + // Bit of subtlety to be able to search records with a view and not a string - the key + // must point at stable memory for the name, which is available in the record itself. + // Therefore the lookup for adding the record must be done using a view based in the record. + // It doesn't matter if it's the IPv4 or IPv6 record that's used, both are stable and equal + // to each other. + // IPv4 + if (auto const &v = std::get<0>(value); v.size() > 0) { + auto r = loader(key, v); + (*map)[r->name_view()].record_4 = r; + } + // IPv6 + if (auto const &v = std::get<1>(value); v.size() > 0) { + auto r = loader(key, v); + (*map)[r->name_view()].record_4 = r; + } + } + + hostdb_hostfile_update_timestamp = hostdb_current_interval; } } // Swap the pointer - if (parsed_hosts_file_ptr != nullptr) { - hostDB.hosts_file_ptr = parsed_hosts_file_ptr; + if (map) { + std::unique_lock lock(hostDB.host_file_mutex); + hostDB.host_file = map; } // Mark this one as completed, so we can allow another update to happen - HostDBFileUpdateActive = 0; + HostDBFileUpdateActive = false; } // @@ -2245,7 +2125,7 @@ struct HostDBRegressionContinuation : public Continuation { int i; int - mainEvent(int event, HostDBInfo *r) + mainEvent(int event, HostDBRecord *r) { (void)event; @@ -2254,23 +2134,15 @@ struct HostDBRegressionContinuation : public Continuation { } if (event == EVENT_HOST_DB_LOOKUP) { --outstanding; - // since this is a lookup done, data is either hostdbInfo or nullptr if (r) { - rprintf(test, "hostdbinfo r=%x\n", r); - rprintf(test, "hostdbinfo hostname=%s\n", r->perm_hostname()); - rprintf(test, "hostdbinfo rr %x\n", r->rr()); + rprintf(test, "HostDBRecord r=%x\n", r); + rprintf(test, "HostDBRecord hostname=%s\n", r->name()); // If RR, print all of the enclosed records - if (r->rr()) { - rprintf(test, "hostdbinfo good=%d\n", r->rr()->good); - for (int x = 0; x < r->rr()->good; x++) { - ip_port_text_buffer ip_buf; - ats_ip_ntop(r->rr()->info(x).ip(), ip_buf, sizeof(ip_buf)); - rprintf(test, "hostdbinfo RR%d ip=%s\n", x, ip_buf); - } - } else { // Otherwise, just the one will do + auto rr_info{r->rr_info()}; + for (int x = 0; x < r->rr_count; ++x) { ip_port_text_buffer ip_buf; - ats_ip_ntop(r->ip(), ip_buf, sizeof(ip_buf)); - rprintf(test, "hostdbinfo A ip=%s\n", ip_buf); + rr_info[x].data.ip.toString(ip_buf, sizeof(ip_buf)); + rprintf(test, "hostdbinfo RR%d ip=%s\n", x, ip_buf); } ++success; } else { @@ -2310,9 +2182,9 @@ struct HostDBRegressionContinuation : public Continuation { static const char *dns_test_hosts[] = { "www.apple.com", "www.ibm.com", "www.microsoft.com", - "www.coke.com", // RR record - "4.2.2.2", // An IP-- since we don't expect resolution - "127.0.0.1", // loopback since it has some special handling + "yahoo.com", // RR record + "4.2.2.2", // An IP-- since we don't expect resolution + "127.0.0.1", // loopback since it has some special handling }; REGRESSION_TEST(HostDBProcessor)(RegressionTest *t, int atype, int *pstatus) @@ -2321,3 +2193,219 @@ REGRESSION_TEST(HostDBProcessor)(RegressionTest *t, int atype, int *pstatus) } #endif +// ----- +void +HostDBRecord::free() +{ + if (_iobuffer_index > 0) { + Debug("hostdb", "freeing %d bytes at [%p]", (1 << (7 + _iobuffer_index)), this); + ioBufAllocator[_iobuffer_index].free_void(static_cast(this)); + } +} + +HostDBRecord * +HostDBRecord::alloc(TextView query_name, unsigned int rr_count, size_t srv_name_size) +{ + const ts::Scalar<8> qn_size = ts::round_up(query_name.size() + 1); + const ts::Scalar<8> r_size = ts::round_up(sizeof(self_type) + qn_size + rr_count * sizeof(HostDBInfo) + srv_name_size); + int iobuffer_index = iobuffer_size_to_index(r_size, hostdb_max_iobuf_index); + ink_release_assert(iobuffer_index >= 0); + auto ptr = ioBufAllocator[iobuffer_index].alloc_void(); + memset(ptr, 0, r_size); + auto self = static_cast(ptr); + new (self) self_type(); + self->_iobuffer_index = iobuffer_index; + self->_record_size = r_size; + + Debug("hostdb", "allocating %ld bytes for %.*s with %d RR records at [%p]", r_size.value(), int(query_name.size()), + query_name.data(), rr_count, self); + + // where in our block of memory we are + int offset = sizeof(self_type); + memcpy(self->apply_offset(offset), query_name); + offset += qn_size; + self->rr_offset = offset; + self->rr_count = rr_count; + // Construct the info instances to a valid state. + for (auto &info : self->rr_info()) { + new (&info) std::remove_reference_t; + } + + return self; +} + +HostDBRecord::self_type * +HostDBRecord::unmarshall(char *buff, unsigned size) +{ + if (size < sizeof(self_type)) { + return nullptr; + } + auto src = reinterpret_cast(buff); + ink_release_assert(size == src->_record_size); + auto ptr = ioBufAllocator[src->_iobuffer_index].alloc_void(); + auto self = static_cast(ptr); + new (self) self_type(); + auto delta = sizeof(RefCountObj); // skip the VFTP and ref count. + memcpy(static_cast(ptr) + delta, buff + delta, size - delta); + return self; +} + +bool +HostDBRecord::serve_stale_but_revalidate() const +{ + // the option is disabled + if (hostdb_serve_stale_but_revalidate <= 0) { + return false; + } + + // ip_timeout_interval == DNS TTL + // hostdb_serve_stale_but_revalidate == number of seconds + // ip_interval() is the number of seconds between now() and when the entry was inserted + if ((ip_timeout_interval + ts_seconds(hostdb_serve_stale_but_revalidate)) > ip_interval()) { + Debug("hostdb", "serving stale entry %ld | %d | %ld as requested by config", ip_timeout_interval.count(), + hostdb_serve_stale_but_revalidate, ip_interval().count()); + return true; + } + + // otherwise, the entry is too old + return false; +} + +HostDBInfo * +HostDBRecord::select_best_srv(char *target, InkRand *rand, ts_time now, ts_seconds fail_window) +{ + ink_assert(rr_count <= 0 || static_cast(rr_count) > hostdb_round_robin_max_count); + + int i = 0; + int live_n = 0; + uint32_t weight = 0, p = INT32_MAX; + HostDBInfo *result = nullptr; + auto rr = this->rr_info(); + // Array of live targets, sized by @a live_n + HostDBInfo *live[rr.count()]; + for (auto &target : rr) { + // skip dead upstreams. + if (rr[i].is_dead(now, fail_window)) { + continue; + } + + if (target.data.srv.srv_priority <= p) { + p = target.data.srv.srv_priority; + weight += target.data.srv.srv_weight; + live[live_n++] = ⌖ + } else { + break; + } + }; + + if (live_n == 0 || weight == 0) { // no valid or weighted choice, use strict RR + result = this->select_next_rr(now, fail_window); + } else { + uint32_t xx = rand->random() % weight; + for (i = 0; i < live_n - 1 && xx >= live[i]->data.srv.srv_weight; ++i) + xx -= live[i]->data.srv.srv_weight; + + result = live[i]; + } + + if (result) { + ink_strlcpy(target, this->name(), MAXDNAME); + return result; + } + return nullptr; +} + +HostDBInfo * +HostDBRecord::select_next_rr(ts_time now, ts_seconds fail_window) +{ + auto rr_info = this->rr_info(); + for (unsigned idx = 0, limit = rr_info.count(); idx < limit; ++idx) { + auto &target = rr_info[this->next_rr()]; + if (target.select(now, fail_window)) { + return ⌖ + } + } + + return nullptr; +} + +unsigned +HostDBRecord::next_rr() +{ + auto raw_idx = ++_rr_idx; + // Modulus on an atomic is a bit tricky - need to make sure the value is always decremented by the + // modulus even if another thread incremented. Update to modulus value iff the value hasn't been + // incremented elsewhere. Eventually the "last" incrementer will do the update. + auto idx = raw_idx % rr_count; + _rr_idx.compare_exchange_weak(raw_idx, idx); + return idx; +} + +HostDBInfo * +HostDBRecord::find(sockaddr const *addr) +{ + for (auto &item : this->rr_info()) { + if (item.data.ip == addr) { + return &item; + } + } + return nullptr; +} + +bool +ResolveInfo::resolve_immediate() +{ + ts::LocalBufferWriter<256> bw; + + if (resolved_p) { + // nothing - already resolved. + } else if (IpAddr tmp; TS_SUCCESS == tmp.load(lookup_name)) { + bw.print("[resolve_immediate] success - FQDN '{}' is a valid IP address.", lookup_name); + Debug("hostdb", "%.*s", int(bw.size()), bw.data()); + addr.assign(tmp); + resolved_p = true; + } + return resolved_p; +} + +bool +ResolveInfo::set_active(HostDBInfo *info) +{ + active = info; + if (info) { + addr.assign(active->data.ip); + resolved_p = true; + if (info->last_fail_time() != TS_TIME_ZERO) { + zombie_p = true; + } + return true; + } + resolved_p = false; + return false; +} + +bool +ResolveInfo::select_next_rr() +{ + if (active) { + if (auto rr_info{this->record->rr_info()}; rr_info.count() > 1) { + unsigned limit = active - rr_info.data(), idx = (limit + 1) % rr_info.count(); + while ((idx = (idx + 1) % rr_info.count()) != limit && !rr_info[idx].is_alive()) + ; + active = &rr_info[idx]; + return idx != limit; // if the active record was actually changed. + } + } + return false; +} + +bool +ResolveInfo::set_upstream_address(IpAddr const &ip_addr) +{ + if (ip_addr.isValid()) { + addr.assign(ip_addr); + resolved_p = true; + return true; + } + return false; +} diff --git a/iocore/hostdb/I_HostDBProcessor.h b/iocore/hostdb/I_HostDBProcessor.h index 06bde6232ad..2877969b470 100644 --- a/iocore/hostdb/I_HostDBProcessor.h +++ b/iocore/hostdb/I_HostDBProcessor.h @@ -23,10 +23,14 @@ #pragma once +#include +#include + #include "tscore/HashFNV.h" #include "tscore/ink_time.h" #include "tscore/CryptoHash.h" #include "tscore/ink_align.h" +#include "tscore/ink_inet.h" #include "tscore/ink_resolver.h" #include "tscore/HTTPVersion.h" #include "I_EventSystem.h" @@ -45,7 +49,8 @@ // // Data // -struct HostDBContinuation; +class HostDBContinuation; +struct ResolveInfo; // // The host database stores host information, most notably the @@ -56,7 +61,7 @@ struct HostDBContinuation; // disk representation to decrease # of seeks. // extern int hostdb_enable; -extern ink_time_t hostdb_current_interval; +extern ts_time hostdb_current_interval; extern unsigned int hostdb_ip_stale_interval; extern unsigned int hostdb_ip_timeout_interval; extern unsigned int hostdb_ip_fail_timeout_interval; @@ -84,339 +89,425 @@ makeHostHash(const char *string) // Types // -/** Host information metadata used by various parts of HostDB. - * It is stored as generic data in the cache. - * - * As a @c union only one of the members is valid, Which one depends on context data in the - * @c HostDBInfo. This data is written literally to disk therefore if any change is made, - * the @c object_version for the cache must be updated by modifying @c HostDBInfo::version. - * - * @see HostDBInfo::version - */ -union HostDBApplicationInfo { - /// Generic storage. This is verified to be the size of the union. - struct application_data_allotment { - unsigned int application1; - unsigned int application2; - } allotment; - - ////////////////////////////////////////////////////////// - // http server attributes in the host database // - // // - // http_version - one of HTTPVersion // - // last_failure - UNIX time for the last time // - // we tried the server & failed // - // fail_count - Number of times we tried and // - // and failed to contact the host // - ////////////////////////////////////////////////////////// - struct http_server_attr { - uint32_t last_failure; - HTTPVersion http_version; - uint8_t fail_count; - http_server_attr() : http_version() {} - } http_data; - - struct application_data_rr { - unsigned int offset; - } rr; - HostDBApplicationInfo() : http_data() {} -}; - -struct HostDBRoundRobin; +class HostDBRecord; +/// Information for an SRV record. struct SRVInfo { - unsigned int srv_offset : 16; + unsigned int srv_offset : 16; ///< Memory offset from @c HostDBInfo to name. unsigned int srv_weight : 16; unsigned int srv_priority : 16; unsigned int srv_port : 16; unsigned int key; }; -struct HostDBInfo : public RefCountObj { - /** Internal IP address data. - This is at least large enough to hold an IPv6 address. - */ +/// Type of data stored. +enum class HostDBType : uint8_t { + UNSPEC, ///< No valid data. + ADDR, ///< IP address. + SRV, ///< SRV record. + HOST ///< Hostname (reverse DNS) +}; +char const *name_of(HostDBType t); - static HostDBInfo * - alloc(int size = 0) - { - size += sizeof(HostDBInfo); - int iobuffer_index = iobuffer_size_to_index(size, hostdb_max_iobuf_index); - ink_release_assert(iobuffer_index >= 0); - void *ptr = ioBufAllocator[iobuffer_index].alloc_void(); - memset(ptr, 0, size); - HostDBInfo *ret = new (ptr) HostDBInfo(); - ret->_iobuffer_index = iobuffer_index; - return ret; - } +/** Information about a single target. + */ +struct HostDBInfo { + using self_type = HostDBInfo; ///< Self reference type. + + /// State of this upstream target. + enum Status : uint8_t { + /// Ready for connections, no failure. + ALIVE = 0, + /// Dead but past blackout time - limit to single connection attempts. + ZOMBIE = 1, + /// Dead, no connections allowed. + DEAD = 2, + }; - void - free() override - { - ink_release_assert(from_alloc()); - Debug("hostdb", "freeing %d bytes at [%p]", (1 << (7 + _iobuffer_index)), this); - ioBufAllocator[_iobuffer_index].free_void((void *)(this)); - } + /// Default constructor. + HostDBInfo() = default; - /// Effectively the @c object_version for cache data. - /// This is used to indicate incompatible changes in the binary layout of HostDB records. - /// It must be updated if any such change is made, even if it is functionally equivalent. - static ts::VersionNumber - version() - { - /// - 1.0 Initial version. - /// - 1.1 tweak HostDBApplicationInfo::http_data. - return ts::VersionNumber(1, 1); - } + HostDBInfo &operator=(HostDBInfo const &that); - static HostDBInfo * - unmarshall(char *buf, unsigned int size) - { - if (size < sizeof(HostDBInfo)) { - return nullptr; - } - HostDBInfo *ret = HostDBInfo::alloc(size - sizeof(HostDBInfo)); - int buf_index = ret->_iobuffer_index; - memcpy((void *)ret, buf, size); - // Reset the refcount back to 0, this is a bit ugly-- but I'm not sure we want to expose a method - // to mess with the refcount, since this is a fairly unique use case - ret = new (ret) HostDBInfo(); - ret->_iobuffer_index = buf_index; - return ret; - } + /// Absolute time of when this target failed. + /// A value of zero (@c TS_TIME_ZERO ) indicates no failure. + ts_time last_fail_time() const; - // return expiry time (in seconds since epoch) - ink_time_t - expiry_time() const - { - return ip_timestamp + ip_timeout_interval + hostdb_serve_stale_but_revalidate; - } + /// Target is alive - no known failure. + bool is_alive(); - sockaddr * - ip() - { - return &data.ip.sa; - } + /// Target has failed and is still in the blocked time window. + bool is_dead(ts_time now, ts_seconds fail_window); - sockaddr const * - ip() const - { - return &data.ip.sa; - } + /** Select this target. + * + * @param now Current time. + * @param fail_window Failure window. + * @return Status of the selection. + * + * If a zombie is selected the failure time is updated to make it look dead to other threads in a thread safe + * manner. The caller should check @c last_fail_time to see if a zombie was selected. + */ + bool select(ts_time now, ts_seconds fail_window); - char *hostname() const; - char *perm_hostname() const; - char *srvname(HostDBRoundRobin *rr) const; + /// Check if this info is valid. + bool is_valid() const; - /// Check if this entry is an element of a round robin entry. - /// If @c true then this entry is part of and was obtained from a round robin root. This is useful if the - /// address doesn't work - a retry can probably get a new address by doing another lookup and resolving to - /// a different element of the round robin. - bool - is_rr_elt() const - { - return 0 != round_robin_elt; - } + /// Mark this info as invalid. + void invalidate(); - HostDBRoundRobin *rr(); + /** Mark the entry as down. + * + * @param now Time of the failure. + * @return @c true if @a this was marked down, @c false if not. + * + * This can return @c false if the entry is already marked down, in which case the failure time is not updated. + */ + bool mark_down(ts_time now); - unsigned int - ip_interval() const - { - return (hostdb_current_interval - ip_timestamp) & 0x7FFFFFFF; - } + /** Mark the target as up / alive. + * + * @return Previous alive state of the target. + */ + bool mark_up(); - int - ip_time_remaining() const - { - return static_cast(ip_timeout_interval) - static_cast(this->ip_interval()); - } + char const *srvname() const; - bool - is_ip_stale() const - { - return ip_timeout_interval >= 2 * hostdb_ip_stale_interval && ip_interval() >= hostdb_ip_stale_interval; - } + /** Migrate data after a DNS update. + * + * @param that Source item. + * + * This moves only specific state information, it is not a generic copy. + */ + void migrate_from(self_type const &that); - bool - is_ip_timeout() const - { - return ip_interval() >= ip_timeout_interval; - } + /// A target is either an IP address or an SRV record. + /// The type should be indicated by @c flags.f.is_srv; + union { + IpAddr ip; ///< IP address / port data. + SRVInfo srv; ///< SRV record. + } data{IpAddr{}}; + + /// Data that migrates after updated DNS records are processed. + /// @see migrate_from + /// @{ + /// Last time a failure was recorded. + std::atomic last_failure{TS_TIME_ZERO}; + /// Count of connection failures + std::atomic fail_count{0}; + /// Expected HTTP version of the target based on earlier transactions. + HTTPVersion http_version = HTTP_INVALID; + /// @} + + self_type &assign(IpAddr const &addr); + +protected: + self_type &assign(sa_family_t af, void const *addr); + self_type &assign(SRV const *srv, char const *name); + + HostDBType type = HostDBType::UNSPEC; ///< Invalid data. + + friend HostDBContinuation; +}; - bool - is_ip_fail_timeout() const - { - return ip_interval() >= hostdb_ip_fail_timeout_interval; +inline HostDBInfo & +HostDBInfo::operator=(HostDBInfo const &that) +{ + if (this != &that) { + memcpy(static_cast(this), static_cast(&that), sizeof(*this)); } + return *this; +} - void - refresh_ip() - { - ip_timestamp = hostdb_current_interval; - } +inline ts_time +HostDBInfo::last_fail_time() const +{ + return last_failure; +} - bool - serve_stale_but_revalidate() const - { - // the option is disabled - if (hostdb_serve_stale_but_revalidate <= 0) { - return false; - } +inline bool +HostDBInfo::is_alive() +{ + return this->last_fail_time() == TS_TIME_ZERO; +} - // ip_timeout_interval == DNS TTL - // hostdb_serve_stale_but_revalidate == number of seconds - // ip_interval() is the number of seconds between now() and when the entry was inserted - if ((ip_timeout_interval + hostdb_serve_stale_but_revalidate) > ip_interval()) { - Debug("hostdb", "serving stale entry %d | %d | %d as requested by config", ip_timeout_interval, - hostdb_serve_stale_but_revalidate, ip_interval()); - return true; - } +inline bool +HostDBInfo::is_dead(ts_time now, ts_seconds fail_window) +{ + auto last_fail = this->last_fail_time(); + return (last_fail != TS_TIME_ZERO) && (last_fail + fail_window < now); +} + +inline bool +HostDBInfo::mark_up() +{ + auto t = last_failure.exchange(TS_TIME_ZERO); + return t != TS_TIME_ZERO; +} + +inline bool +HostDBInfo::mark_down(ts_time now) +{ + auto t0{TS_TIME_ZERO}; + return last_failure.compare_exchange_strong(t0, now); +} - // otherwise, the entry is too old - return false; +inline bool +HostDBInfo::select(ts_time now, ts_seconds fail_window) +{ + auto t0 = this->last_fail_time(); + if (t0 == TS_TIME_ZERO) { + return true; // it's alive and so is valid for selection. } + // Success means this is a zombie and this thread updated the failure time. + return (t0 + fail_window < now) && last_failure.compare_exchange_strong(t0, now); +} - /* - * Given the current time `now` and the fail_window, determine if this real is alive +inline void +HostDBInfo::migrate_from(HostDBInfo::self_type const &that) +{ + this->last_failure = that.last_failure.load(); + this->http_version = that.http_version; +} + +inline bool +HostDBInfo::is_valid() const +{ + return type != HostDBType::UNSPEC; +} + +inline void +HostDBInfo::invalidate() +{ + type = HostDBType::UNSPEC; +} + +// ---- +/** Root item for HostDB. + * This is the container for HostDB data. It is always an array of @c HostDBInfo instances plus metadata. + * All strings are C-strings and therefore don't need a distinct size. + * + */ +class HostDBRecord : public RefCountObj +{ + friend class HostDBContinuation; + friend struct ShowHostDB; + using self_type = HostDBRecord; + + /// Size of the IO buffer block owned by @a this. + /// If negative @a this is in not allocated memory. + int _iobuffer_index{-1}; + /// Actual size of the data. + unsigned _record_size = sizeof(self_type); + +public: + HostDBRecord() = default; + HostDBRecord(self_type const &that) = delete; + + using Handle = Ptr; ///< Shared pointer type to hold an instance. + + /** Allocate an instance from the IOBuffers. + * + * @param query_name Name of the query for the record. + * @param rr_count Number of info instances. + * @param srv_name_size Storage for SRV names, if any. + * @return An instance sufficient to hold the specified data. + * + * The query name will stored and initialized, and the info instances initialized. */ - bool - is_alive(ink_time_t now, int32_t fail_window) - { - unsigned int last_failure = app.http_data.last_failure; - - if (last_failure == 0 || (unsigned int)(now - fail_window) > last_failure) { - return true; - } else { - // Entry is marked down. Make sure some nasty clock skew - // did not occur. Use the retry time to set an upper bound - // as to how far in the future we should tolerate bogus last - // failure times. This sets the upper bound that we would ever - // consider a server down to 2*down_server_timeout - if ((unsigned int)(now + fail_window) < last_failure) { - app.http_data.last_failure = 0; - return false; - } - return false; - } - } + static self_type *alloc(ts::TextView query_name, unsigned rr_count, size_t srv_name_size = 0); - bool - is_failed() const - { - return !((is_srv && data.srv.srv_offset) || (reverse_dns && data.hostname_offset) || ats_is_ip(ip())); - } + /// Type of data stored in this record. + HostDBType record_type = HostDBType::UNSPEC; - void - set_failed() - { - if (is_srv) { - data.srv.srv_offset = 0; - } else if (reverse_dns) { - data.hostname_offset = 0; - } else { - ats_ip_invalidate(ip()); - } - } + /// IP family of this record. + sa_family_t af_family = AF_UNSPEC; + + /// Offset from @a this to the VLA. + unsigned short rr_offset = 0; + + /// Number of @c HostDBInfo instances. + unsigned short rr_count = 0; + /// Timing data for switch records in the RR. + std::atomic rr_ctime{TS_TIME_ZERO}; + + /// Hash key. uint64_t key; - // Application specific data. NOTE: We need an integral number of - // these per block. This structure is 32 bytes. (at 200k hosts = - // 8 Meg). Which gives us 7 bytes of application information. - HostDBApplicationInfo app; + /// When the data was received. + ts_time ip_timestamp; - union { - IpEndpoint ip; ///< IP address / port data. - unsigned int hostname_offset; ///< Some hostname thing. - SRVInfo srv; - } data; + /// Valid duration of the data. + ts_seconds ip_timeout_interval; - unsigned int hostname_offset; // always maintain a permanent copy of the hostname for non-rev dns records. + /** Atomically advance the round robin index. + * + * If multiple threads call this simultaneously each thread will get a distinct return value. + * + * @return The new round robin index. + */ + unsigned next_rr(); + + /** Pick the next round robin and update the record atomically. + * + * @note This may select a zombie server and reserve it for the caller, therefore the caller must + * attempt to connect to the selected target if possible. + * + * @param now Current time to use for aliveness calculations. + * @param fail_window Blackout time for dead servers. + * @return Status of the updated target. + * + * If the return value is @c HostDBInfo::Status::DEAD this means all targets are dead and there is + * no valid upstream. + * + * @note Concurrency - this is not done under lock and depends on the caller for correct use. + * For strict round robin, it is a feature that every call will get a distinct index. For + * timed round robin, the caller must arrange to have only one thread call this per time interval. + */ + HostDBInfo *select_next_rr(ts_time now, ts_seconds fail_window); - unsigned int ip_timestamp; + /// Check if this record is of SRV targets. + bool is_srv() const; - unsigned int ip_timeout_interval; // bounded between 1 and HOST_DB_MAX_TTL (0x1FFFFF, 24 days) + /** Query name for the record. + * @return A C-string. + * If this is a @c HOST record, this is the resolved named and the query was based on the IP address. + * Otherwise this is the name used in the DNS query. + */ + char const *name() const; + + /** Query name for the record. + * @return A view. + * If this is a @c HOST record, this is the resolved named and the query was based on the IP address. + * Otherwise this is the name used in the DNS query. + * @note Although not included in the view, the name is always nul terminated and the string can + * be used as a C-string. + */ + ts::TextView name_view() const; - unsigned int is_srv : 1; - unsigned int reverse_dns : 1; + /// Get the array of info instances. + ts::MemSpan rr_info(); - unsigned int round_robin : 1; // This is the root of a round robin block - unsigned int round_robin_elt : 1; // This is an address in a round robin block + /** Find a host record by IP address. + * + * @param addr Address key. + * @return A pointer to the info instance if a match is found, @c nullptr if not. + */ + HostDBInfo *find(sockaddr const *addr); + + /** Select an upstream target. + * + * @param now Current time. + * @param fail_window Dead server blackout time. + * @param hash_addr Inbound remote IP address. + * @return A selected target, or @c nullptr if there are no valid targets. + * + * This accounts for the round robin setting. The default is to use "client affinity" in + * which case @a hash_addr is as a hash seed to select the target. + * + * This may select a zombie target, which can be detected by checking the target's last + * failure time. If it is not @c TS_TIME_ZERO the target is a zombie. Other transactions will + * be blocked from selecting that target until @a fail_window time has passed. + * + * In cases other than strict round robin, a base target is selected. If valid, that is returned, + * but if not then the targets in this record are searched until a valid one is found. The result + * is this can be called to select a target for failover when a previous target fails. + */ + HostDBInfo *select_best_http(ts_time now, ts_seconds fail_window, sockaddr const *hash_addr); + HostDBInfo *select_best_srv(char *target, InkRand *rand, ts_time now, ts_seconds fail_window); - HostDBInfo() : _iobuffer_index{-1} {} + bool is_failed() const; - HostDBInfo(HostDBInfo const &src) : RefCountObj() - { - memcpy(static_cast(this), static_cast(&src), sizeof(*this)); - _iobuffer_index = -1; - } + void set_failed(); - HostDBInfo & - operator=(HostDBInfo const &src) - { - if (this != &src) { - int iob_idx = _iobuffer_index; - memcpy(static_cast(this), static_cast(&src), sizeof(*this)); - _iobuffer_index = iob_idx; - } - return *this; - } + /// @return The time point when the item expires. + ts_time expiry_time() const; - bool - from_alloc() const - { - return _iobuffer_index >= 0; - } + ts_seconds ip_interval() const; -private: - // The value of this will be -1 for objects that are not created by the alloc() static member function. - int _iobuffer_index; -}; + ts_seconds ip_time_remaining() const; + + bool is_ip_stale() const; + + bool is_ip_timeout() const; + + bool is_ip_fail_timeout() const; + + void refresh_ip(); + + bool serve_stale_but_revalidate() const; -struct HostDBRoundRobin { - /** Total number (to compute space used). */ - short rrcount = 0; + /// Deallocate @a this. + void free() override; - /** Number which have not failed a connect. */ - short good = 0; + /** The current round robin index. + * + * @return The current index. + * + * @note The internal index may be out of range due to concurrency constraints - this insures the + * returned valu is in range. + */ + unsigned short rr_idx() const; + + /** Offset from the current round robin index. + * + * @param delta Distance from the current index. + * @return The effective index. + */ + unsigned short rr_idx(unsigned short delta) const; - unsigned short current = 0; - ink_time_t timed_rr_ctime = 0; + /// The index of @a target in this record. + int index_of(HostDBInfo const *target) const; - // This is the equivalent of a variable length array, we can't use a VLA because - // HostDBInfo is a non-POD type-- so this is the best we can do. - HostDBInfo & - info(short n) + /** Allocation and initialize an instance from a serialized buffer. + * + * @param buff Serialization data. + * @param size Size of @a buff. + * @return An instance initialized from @a buff. + */ + static self_type *unmarshall(char *buff, unsigned size); + + /// Database version. + static constexpr ts::VersionNumber Version{3, 0}; + +protected: + /// Current active info. + /// @note This value may be out of range due to the difficulty of synchronization, therefore + /// must always be taken modulus @c rr_count when used. Use the @c rr_idx() method unless + /// raw access is required. + std::atomic _rr_idx = 0; + + /** Access an internal object at @a offset. + * + * @tparam T Type of object. + * @param offset Offset of object. + * @return A pointer to the object of type @a T. + * + * @a offset is applied to @a this record and the result cast to a pointer to @a T. + * + * @note @a offset based at @a this. + */ + template + T * + apply_offset(unsigned offset) { - ink_assert(n < rrcount && n >= 0); - return *((HostDBInfo *)((char *)this + sizeof(HostDBRoundRobin)) + n); + return reinterpret_cast(reinterpret_cast(this) + offset); } - // Return the allocation size of a HostDBRoundRobin struct suitable for storing - // "count" HostDBInfo records. - static unsigned - size(unsigned count, unsigned srv_len = 0) + template + T const * + apply_offset(unsigned offset) const { - ink_assert(count > 0); - return INK_ALIGN((sizeof(HostDBRoundRobin) + (count * sizeof(HostDBInfo)) + srv_len), 8); + return reinterpret_cast(reinterpret_cast(this) + offset); } - /** Find the index of @a addr in member @a info. - @return The index if found, -1 if not found. - */ - int index_of(sockaddr const *addr); - HostDBInfo *find_ip(sockaddr const *addr); - // Find the srv target - HostDBInfo *find_target(const char *target); - /** Select the next entry after @a addr. - @note If @a addr isn't an address in the round robin nothing is updated. - @return The selected entry or @c nullptr if @a addr wasn't present. - */ - HostDBInfo *select_next(sockaddr const *addr); - HostDBInfo *select_best_http(sockaddr const *client_ip, ink_time_t now, int32_t fail_window); - HostDBInfo *select_best_srv(char *target, InkRand *rand, ink_time_t now, int32_t fail_window); - HostDBRoundRobin() {} + union { + uint16_t all; + struct { + unsigned failed_p : 1; ///< DNS error. + } f; + } flags{0}; }; struct HostDBCache; @@ -424,10 +515,125 @@ struct HostDBHash; // Prototype for inline completion function or // getbyname_imm() -typedef void (Continuation::*cb_process_result_pfn)(HostDBInfo *r); +using cb_process_result_pfn = void (Continuation::*)(HostDBRecord *r); Action *iterate(Continuation *cont); +/** Information for doing host resolution for a request. + * + * This is effectively a state object for a request attempting to connect upstream. Information about its attempt + * that are local to the request are kept here, while shared data is accessed via the @c HostDBInfo pointers. + * + * A primitive version of the IP address generator concept. + */ +struct ResolveInfo { + using self_type = ResolveInfo; ///< Self reference type. + + /// Not quite sure what this is for. + enum UpstreamResolveStyle { UNDEFINED_LOOKUP, ORIGIN_SERVER, PARENT_PROXY, HOST_NONE }; + + /** Origin server address source selection. + + If config says to use CTA (client target addr) state is TRY_CLIENT, otherwise it + remains the default. If the connect fails then we switch to a USE. We go to USE_HOSTDB if (1) + the HostDB lookup is successful and (2) some address other than the CTA is available to try. + Otherwise we keep retrying on the CTA (USE_CLIENT) up to the max retry value. In essence we + try to treat the CTA as if it were another RR value in the HostDB record. + */ + enum class OS_Addr { + TRY_DEFAULT, ///< Initial state, use what config says. + TRY_HOSTDB, ///< Try HostDB data. + TRY_CLIENT, ///< Try client target addr. + USE_HOSTDB, ///< Force use of HostDB target address. + USE_CLIENT, ///< Force client target addr. + USE_API ///< Use the API provided address. + }; + + ResolveInfo() = default; + + /// Keep a reference to the base HostDB object, so it doesn't get GC'd. + Ptr record; + HostDBInfo *active = nullptr; ///< Active host record. + + /// Working address. The meaning / source of the value depends on other elements. + /// This is the "resolved" address if @a resolved_p is @c true. + IpEndpoint addr; + + int attempts = 0; ///< Number of connection attempts. + ts_seconds fail_window{300}; ///< Down server blackout time (txn overridable) + + char const *lookup_name = nullptr; + char srv_hostname[MAXDNAME] = {0}; + const sockaddr *inbound_remote_addr = nullptr; ///< Remote address of inbound client - used for hashing. + // const sockaddr *client_target_addr = nullptr; ///< Set if trying a transparent connection. + in_port_t srv_port = 0; ///< Port from SRV lookup or API call. + + OS_Addr os_addr_style = OS_Addr::TRY_DEFAULT; + HostResStyle host_res_style = HOST_RES_IPV4; + UpstreamResolveStyle looking_up = UNDEFINED_LOOKUP; + + HTTPVersion http_version = HTTP_INVALID; + + bool resolved_p = false; ///< If there is a valid, resolved address in @a addr. + + /// Flag for @a addr being set externally. + // bool api_addr_set_p = false; + + /*** Set to true by default. If use_client_target_address is set + * to 1, this value will be set to false if the client address is + * not in the DNS pool */ + bool cta_validated_p = true; + + /// If the target was selected while failed. + bool zombie_p = false; + + bool set_active(HostDBInfo *info); + + bool set_active(sockaddr const *s); + + bool set_active(std::nullptr_t); + + /** Force a resolved address. + * + * @param sa Address to use for the upstream. + * @return @c true if successful, @c false if error. + * + * This fails if @a sa isn't a valid IP address. + */ + bool set_upstream_address(const sockaddr *sa); + + bool set_upstream_address(IpAddr const &ip_addr); + + void set_upstream_port(in_port_t port); + + /** Check and (if possible) immediately resolve the upstream address without consulting the HostDB. + * The cases where this is successful are + * - The address is already resolved (@a resolved_p is @c true). + * - The upstream was set explicitly. + * - The hostname is a valid IP address. + * + * @return @c true if the upstream address was resolved, @c false if not. + */ + bool resolve_immediate(); + + /** Mark the active target as dead. + * + * @param now Time of failure. + * @return @c true if the server was marked as dead, @c false if not. + * + */ + bool mark_active_server_dead(ts_time now); + + /** Mark the active target as alive. + * + * @return @c true if the target changed state. + */ + bool mark_active_server_alive(); + + /// Select / resolve to the next RR entry for the record. + bool select_next_rr(); +}; + /** The Host Database access interface. */ struct HostDBProcessor : public Processor { friend struct HostDBSync; @@ -486,29 +692,6 @@ struct HostDBProcessor : public Processor { /** Lookup Hostinfo by addr */ Action *getbyaddr_re(Continuation *cont, sockaddr const *aip); - /** Set the application information (fire-and-forget). */ - void - setbyname_appinfo(char *hostname, int len, int port, HostDBApplicationInfo *app) - { - sockaddr_in addr; - ats_ip4_set(&addr, INADDR_ANY, port); - setby(hostname, len, ats_ip_sa_cast(&addr), app); - } - - void - setbyaddr_appinfo(sockaddr const *addr, HostDBApplicationInfo *app) - { - this->setby(nullptr, 0, addr, app); - } - - void - setbyaddr_appinfo(in_addr_t ip, HostDBApplicationInfo *app) - { - sockaddr_in addr; - ats_ip4_set(&addr, ip); - this->setby(nullptr, 0, ats_ip_sa_cast(&addr), app); - } - /** Configuration. */ static int hostdb_strict_round_robin; static int hostdb_timed_round_robin; @@ -524,21 +707,150 @@ struct HostDBProcessor : public Processor { private: Action *getby(Continuation *cont, cb_process_result_pfn cb_process_result, HostDBHash &hash, Options const &opt); +}; -public: - /** Set something. - @a aip can carry address and / or port information. If setting just - by a port value, the address should be set to INADDR_ANY which is of - type IPv4. - */ - void setby(const char *hostname, ///< Hostname. - int len, ///< Length of hostname. - sockaddr const *aip, ///< Address and/or port. - HostDBApplicationInfo *app ///< I don't know. - ); +inline bool +HostDBRecord::is_srv() const +{ + return HostDBType::SRV == record_type; +} - void setby_srv(const char *hostname, int len, const char *target, HostDBApplicationInfo *app); -}; +inline char const * +HostDBRecord::name() const +{ + return this->apply_offset(sizeof(self_type)); +} + +inline ts::TextView +HostDBRecord::name_view() const +{ + return {this->name(), ts::TextView::npos}; +} + +inline ts_time +HostDBRecord::expiry_time() const +{ + return ip_timestamp + ip_timeout_interval + ts_seconds(hostdb_serve_stale_but_revalidate); +} + +inline ts_seconds +HostDBRecord::ip_interval() const +{ + static constexpr ts_seconds ZERO{0}; + static constexpr ts_seconds MAX{0x7FFFFFFF}; + return std::clamp(std::chrono::duration_cast((hostdb_current_interval - ip_timestamp)), ZERO, MAX); +} + +inline ts_seconds +HostDBRecord::ip_time_remaining() const +{ + return ip_timeout_interval - this->ip_interval(); +} + +inline bool +HostDBRecord::is_ip_stale() const +{ + return ip_timeout_interval >= ts_seconds(2 * hostdb_ip_stale_interval) && ip_interval() >= ts_seconds(hostdb_ip_stale_interval); +} + +inline bool +HostDBRecord::is_ip_timeout() const +{ + return ip_interval() >= ip_timeout_interval; +} + +inline bool +HostDBRecord::is_ip_fail_timeout() const +{ + return ip_interval() >= ts_seconds(hostdb_ip_fail_timeout_interval); +} + +inline void +HostDBRecord::refresh_ip() +{ + ip_timestamp = hostdb_current_interval; +} + +inline ts::MemSpan +HostDBRecord::rr_info() +{ + return {this->apply_offset(rr_offset), rr_count}; +} + +inline bool +HostDBRecord::is_failed() const +{ + return flags.f.failed_p; +} + +inline void +HostDBRecord::set_failed() +{ + flags.f.failed_p = true; +} + +inline unsigned short +HostDBRecord::rr_idx() const +{ + return _rr_idx % rr_count; +} + +inline unsigned short +HostDBRecord::rr_idx(unsigned short delta) const +{ + return (_rr_idx + delta) % rr_count; +} + +inline int +HostDBRecord::index_of(HostDBInfo const *target) const +{ + return target ? target - this->apply_offset(rr_offset) : -1; +} + +// -- + +inline bool +ResolveInfo::set_active(sockaddr const *s) +{ + return this->set_active(record->find(s)); +} + +inline bool +ResolveInfo::mark_active_server_alive() +{ + if (zombie_p && active) { + active->mark_up(); + } + zombie_p = false; + return false; +} + +inline bool +ResolveInfo::mark_active_server_dead(ts_time now) +{ + return active != nullptr && active->mark_down(now); +} + +inline bool ResolveInfo::set_active(std::nullptr_t) +{ + active = nullptr; + resolved_p = false; + return false; +} + +inline bool +ResolveInfo::set_upstream_address(sockaddr const *sa) +{ + return resolved_p = addr.assign(sa).isValid(); +} + +inline void +ResolveInfo::set_upstream_port(in_port_t port) +{ + srv_port = port; +} + +// --- void run_HostDBTest(); diff --git a/iocore/hostdb/P_HostDBProcessor.h b/iocore/hostdb/P_HostDBProcessor.h index 1dbe1a5799b..8e9b76dc7be 100644 --- a/iocore/hostdb/P_HostDBProcessor.h +++ b/iocore/hostdb/P_HostDBProcessor.h @@ -27,6 +27,8 @@ #pragma once +#include + #include "I_HostDBProcessor.h" #include "tscore/TsBuffer.h" @@ -51,7 +53,7 @@ extern int hostdb_ttl_mode; extern int hostdb_srv_enabled; // extern int hostdb_timestamp; -extern int hostdb_sync_frequency; +extern ts_seconds hostdb_sync_frequency; extern int hostdb_disable_reverse_lookup; // Static configuration information @@ -74,26 +76,6 @@ enum HostDBMark { */ extern const char *string_for(HostDBMark mark); -inline unsigned int -HOSTDB_CLIENT_IP_HASH(sockaddr const *lhs, sockaddr const *rhs) -{ - unsigned int zret = ~static_cast(0); - if (ats_ip_are_compatible(lhs, rhs)) { - if (ats_is_ip4(lhs)) { - in_addr_t ip1 = ats_ip4_addr_cast(lhs); - in_addr_t ip2 = ats_ip4_addr_cast(rhs); - zret = (ip1 >> 16) ^ ip1 ^ ip2 ^ (ip2 >> 16); - } else if (ats_is_ip6(lhs)) { - uint32_t const *ip1 = ats_ip_addr32_cast(lhs); - uint32_t const *ip2 = ats_ip_addr32_cast(rhs); - for (int i = 0; i < 4; ++i, ++ip1, ++ip2) { - zret ^= (*ip1 >> 16) ^ *ip1 ^ *ip2 ^ (*ip2 >> 16); - } - } - } - return zret & 0xFFFF; -} - // // Constants // @@ -169,21 +151,12 @@ extern RecRawStatBlock *hostdb_rsb; #define HOSTDB_DECREMENT_THREAD_DYN_STAT(_s, _t) RecIncrRawStatSum(hostdb_rsb, _t, (int)_s, -1); -struct CmpConstBuffferCaseInsensitive { - bool - operator()(ts::ConstBuffer a, ts::ConstBuffer b) const - { - return ptr_len_casecmp(a._ptr, a._size, b._ptr, b._size) < 0; - } +struct HostFileRecord { + HostDBRecord::Handle record_4; + HostDBRecord::Handle record_6; }; -// Our own typedef for the host file mapping -typedef std::map HostsFileMap; -// A to hold a ref-counted map -struct RefCountedHostsFileMap : public RefCountObj { - HostsFileMap hosts_file_map; - ats_scoped_str HostFileText; -}; +using HostFileMap = std::unordered_map>; // // HostDBCache (Private) @@ -191,9 +164,11 @@ struct RefCountedHostsFileMap : public RefCountObj { struct HostDBCache { int start(int flags = 0); // Map to contain all of the host file overrides, initialize it to empty - Ptr hosts_file_ptr; + std::shared_ptr host_file; + std::shared_mutex host_file_mutex; + // TODO: make ATS call a close() method or something on shutdown (it does nothing of the sort today) - RefCountCache *refcountcache = nullptr; + RefCountCache *refcountcache = nullptr; // TODO configurable number of items in the cache Queue *pending_dns = nullptr; @@ -201,189 +176,9 @@ struct HostDBCache { Queue *remoteHostDBQueue = nullptr; HostDBCache(); bool is_pending_dns_for_hash(const CryptoHash &hash); -}; - -inline int -HostDBRoundRobin::index_of(sockaddr const *ip) -{ - bool bad = (rrcount <= 0 || (unsigned int)rrcount > hostdb_round_robin_max_count || good <= 0 || - (unsigned int)good > hostdb_round_robin_max_count); - if (bad) { - ink_assert(!"bad round robin size"); - return -1; - } - - for (int i = 0; i < good; i++) { - if (ats_ip_addr_eq(ip, info(i).ip())) { - return i; - } - } - - return -1; -} - -inline HostDBInfo * -HostDBRoundRobin::find_ip(sockaddr const *ip) -{ - int idx = this->index_of(ip); - return idx < 0 ? nullptr : &info(idx); -} - -inline HostDBInfo * -HostDBRoundRobin::select_next(sockaddr const *ip) -{ - HostDBInfo *zret = nullptr; - if (good > 1) { - int idx = this->index_of(ip); - if (idx >= 0) { - idx = (idx + 1) % good; - zret = &info(idx); - } - } - return zret; -} - -inline HostDBInfo * -HostDBRoundRobin::find_target(const char *target) -{ - bool bad = (rrcount <= 0 || (unsigned int)rrcount > hostdb_round_robin_max_count || good <= 0 || - (unsigned int)good > hostdb_round_robin_max_count); - if (bad) { - ink_assert(!"bad round robin size"); - return nullptr; - } - - uint32_t key = makeHostHash(target); - for (int i = 0; i < good; i++) { - if (info(i).data.srv.key == key && !strcmp(target, info(i).srvname(this))) - return &info(i); - } - return nullptr; -} - -inline HostDBInfo * -HostDBRoundRobin::select_best_http(sockaddr const *client_ip, ink_time_t now, int32_t fail_window) -{ - bool bad = (rrcount <= 0 || (unsigned int)rrcount > hostdb_round_robin_max_count || good <= 0 || - (unsigned int)good > hostdb_round_robin_max_count); - - if (bad) { - ink_assert(!"bad round robin size"); - return nullptr; - } - - int best_any = 0; - int best_up = -1; - - // Basic round robin, increment current and mod with how many we have - if (HostDBProcessor::hostdb_strict_round_robin) { - Debug("hostdb", "Using strict round robin"); - // Check that the host we selected is alive - for (int i = 0; i < good; i++) { - best_any = current++ % good; - if (info(best_any).is_alive(now, fail_window)) { - best_up = best_any; - break; - } - } - } else if (HostDBProcessor::hostdb_timed_round_robin > 0) { - Debug("hostdb", "Using timed round-robin for HTTP"); - if ((now - timed_rr_ctime) > HostDBProcessor::hostdb_timed_round_robin) { - Debug("hostdb", "Timed interval expired.. rotating"); - ++current; - timed_rr_ctime = now; - } - for (int i = 0; i < good; i++) { - best_any = (current + i) % good; - if (info(best_any).is_alive(now, fail_window)) { - best_up = best_any; - break; - } - } - Debug("hostdb", "Using %d for best_up", best_up); - } else { - Debug("hostdb", "Using default round robin"); - unsigned int best_hash_any = 0; - unsigned int best_hash_up = 0; - for (int i = 0; i < good; i++) { - sockaddr const *ip = info(i).ip(); - unsigned int h = HOSTDB_CLIENT_IP_HASH(client_ip, ip); - if (best_hash_any <= h) { - best_any = i; - best_hash_any = h; - } - if (info(i).is_alive(now, fail_window)) { - if (best_hash_up <= h) { - best_up = i; - best_hash_up = h; - } - } - } - } - - if (best_up != -1) { - ink_assert(best_up >= 0 && best_up < good); - return &info(best_up); - } else { - ink_assert(best_any >= 0 && best_any < good); - return &info(best_any); - } -} - -inline HostDBInfo * -HostDBRoundRobin::select_best_srv(char *target, InkRand *rand, ink_time_t now, int32_t fail_window) -{ - bool bad = (rrcount <= 0 || (unsigned int)rrcount > hostdb_round_robin_max_count || good <= 0 || - (unsigned int)good > hostdb_round_robin_max_count); - - if (bad) { - ink_assert(!"bad round robin size"); - return nullptr; - } - -#ifdef DEBUG - for (int i = 1; i < good; ++i) { - ink_assert(info(i).data.srv.srv_priority >= info(i - 1).data.srv.srv_priority); - } -#endif - - int i = 0; - int len = 0; - uint32_t weight = 0, p = INT32_MAX; - HostDBInfo *result = nullptr; - HostDBInfo *infos[good]; - do { - // if the real isn't alive-- exclude it from selection - if (!info(i).is_alive(now, fail_window)) { - continue; - } - - if (info(i).data.srv.srv_priority <= p) { - p = info(i).data.srv.srv_priority; - weight += info(i).data.srv.srv_weight; - infos[len++] = &info(i); - } else - break; - } while (++i < good); - - if (len == 0) { // all failed - result = &info(current++ % good); - } else if (weight == 0) { // srv weight is 0 - result = infos[current++ % len]; - } else { - uint32_t xx = rand->random() % weight; - for (i = 0; i < len - 1 && xx >= infos[i]->data.srv.srv_weight; ++i) - xx -= infos[i]->data.srv.srv_weight; - - result = infos[i]; - } - if (result) { - ink_strlcpy(target, result->srvname(this), MAXDNAME); - return result; - } - return nullptr; -} + std::shared_ptr acquire_host_file(); +}; // // Types @@ -397,10 +192,9 @@ struct HostDBHash { CryptoHash hash; ///< The hash value. - const char *host_name = nullptr; ///< Host name. - int host_len = 0; ///< Length of @a _host_name - IpAddr ip; ///< IP address. - in_port_t port = 0; ///< IP port (host order). + ts::TextView host_name; ///< Name of the host for the query. + IpAddr ip; ///< IP address. + in_port_t port = 0; ///< IP port (host order). /// DNS server. Not strictly part of the hash data but /// it's both used by @c HostDBContinuation and provides access to /// hash data. It's just handier to store it here for both uses. @@ -417,7 +211,12 @@ struct HostDBHash { /** Assign a hostname. This updates the split DNS data as well. */ - self &set_host(const char *name, int len); + self &set_host(ts::TextView name); + self & + set_host(char const *name) + { + return this->set_host(ts::TextView{name, strlen(name)}); + } }; // @@ -429,8 +228,7 @@ typedef int (HostDBContinuation::*HostDBContHandler)(int, void *); struct HostDBContinuation : public Continuation { Action action; HostDBHash hash; - // IpEndpoint ip; - unsigned int ttl = 0; + ts_seconds ttl{0}; // HostDBMark db_mark; ///< Target type. /// Original IP address family style. Note this will disagree with /// @a hash.db_mark when doing a retry on an alternate family. The retry @@ -439,9 +237,8 @@ struct HostDBContinuation : public Continuation { int dns_lookup_timeout = DEFAULT_OPTIONS.timeout; Event *timeout = nullptr; Continuation *from_cont = nullptr; - HostDBApplicationInfo app; - int probe_depth = 0; - size_t current_iterate_pos = 0; + int probe_depth = 0; + size_t current_iterate_pos = 0; // char name[MAXDNAME]; // int namelen; char hash_host_name_store[MAXDNAME + 1]; // used as backing store for @a hash @@ -451,7 +248,6 @@ struct HostDBContinuation : public Continuation { unsigned int missing : 1; unsigned int force_dns : 1; - unsigned int round_robin : 1; int probeEvent(int event, Event *e); int iterateEvent(int event, Event *e); @@ -474,19 +270,23 @@ struct HostDBContinuation : public Continuation { { return hash.db_mark == HOSTDB_MARK_SRV; } - HostDBInfo *lookup_done(IpAddr const &ip, const char *aname, bool round_robin, unsigned int attl, SRVHosts *s = nullptr, - HostDBInfo *r = nullptr); + + Ptr + lookup_done(const char *query_name, ts_seconds answer_ttl, SRVHosts *s = nullptr, Ptr record = Ptr{}) + { + return this->lookup_done(ts::TextView{query_name, strlen(query_name)}, answer_ttl, s, record); + } + + Ptr lookup_done(ts::TextView query_name, ts_seconds answer_ttl, SRVHosts *s = nullptr, + Ptr record = Ptr{}); + int key_partition(); void remove_trigger_pending_dns(); int set_check_pending_dns(); - HostDBInfo *insert(unsigned int attl); - /** Optional values for @c init. */ struct Options { - typedef Options self; ///< Self reference type. - int timeout = 0; ///< Timeout value. Default 0 HostResStyle host_res_style = HOST_RES_NONE; ///< IP address family fallback. Default @c HOST_RES_NONE bool force_dns = false; ///< Force DNS lookup. Default @c false @@ -499,7 +299,7 @@ struct HostDBContinuation : public Continuation { int make_get_message(char *buf, int len); int make_put_message(HostDBInfo *r, Continuation *c, char *buf, int len); - HostDBContinuation() : missing(false), force_dns(DEFAULT_OPTIONS.force_dns), round_robin(false) + HostDBContinuation() : missing(false), force_dns(DEFAULT_OPTIONS.force_dns) { ink_zero(hash_host_name_store); ink_zero(hash.hash); @@ -513,12 +313,6 @@ master_hash(CryptoHash const &hash) return static_cast(hash[1] >> 32); } -inline bool -is_dotted_form_hostname(const char *c) -{ - return -1 != (int)ink_inet_addr(c); -} - inline Queue & HostDBCache::pending_dns_for_hash(const CryptoHash &hash) { diff --git a/plugins/lua/ts_lua_misc.c b/plugins/lua/ts_lua_misc.c index d65ec5ec524..b3e29f8bffc 100644 --- a/plugins/lua/ts_lua_misc.c +++ b/plugins/lua/ts_lua_misc.c @@ -458,7 +458,6 @@ ts_lua_host_lookup_handler(TSCont contp, TSEvent event, void *edata) { ts_lua_async_item *ai; ts_lua_cont_info *ci; - struct sockaddr const *addr; char cip[128]; lua_State *L; ts_lua_coroutine *crt; @@ -477,12 +476,13 @@ ts_lua_host_lookup_handler(TSCont contp, TSEvent event, void *edata) } else if (!edata) { lua_pushnil(L); } else { - TSHostLookupResult result = (TSHostLookupResult)edata; - addr = TSHostLookupResultAddrGet(result); - if (addr->sa_family == AF_INET) { - inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)addr)->sin_addr, cip, sizeof(cip)); + struct sockaddr_storage addr; + TSHostLookupResult record = (TSHostLookupResult)edata; + TSHostLookupResultAddrGet(record, (struct sockaddr *)&addr); + if (addr.ss_family == AF_INET) { + inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)&addr)->sin_addr, cip, sizeof(cip)); } else { - inet_ntop(AF_INET6, (const void *)&((struct sockaddr_in6 *)addr)->sin6_addr, cip, sizeof(cip)); + inet_ntop(AF_INET6, (const void *)&((struct sockaddr_in6 *)&addr)->sin6_addr, cip, sizeof(cip)); } lua_pushstring(L, cip); } diff --git a/proxy/ControlMatcher.h b/proxy/ControlMatcher.h index 6c1a3cbdae7..b18cf4cfbb4 100644 --- a/proxy/ControlMatcher.h +++ b/proxy/ControlMatcher.h @@ -144,10 +144,10 @@ class HttpRequestData : public RequestData ink_zero(dest_ip); } - HTTPHdr *hdr = nullptr; - char *hostname_str = nullptr; - HttpApiInfo *api_info = nullptr; - time_t xact_start = 0; + HTTPHdr *hdr = nullptr; + char const *hostname_str = nullptr; + HttpApiInfo *api_info = nullptr; + time_t xact_start = 0; IpEndpoint src_ip; IpEndpoint dest_ip; uint16_t incoming_port = 0; diff --git a/proxy/ParentSelection.cc b/proxy/ParentSelection.cc index f39686aa815..aeb3dc6d7eb 100644 --- a/proxy/ParentSelection.cc +++ b/proxy/ParentSelection.cc @@ -1069,21 +1069,21 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, params = new ParentConfigParams(ParentTable); \ } while (0) -#define REINIT \ - do { \ - if (request != NULL) { \ - delete request->hdr; \ - ats_free(request->hostname_str); \ - delete request->api_info; \ - } \ - delete request; \ - delete result; \ - request = new HttpRequestData(); \ - result = new ParentResult(); \ - if (!result || !request) { \ - (void)printf("Allocation failed\n"); \ - return; \ - } \ +#define REINIT \ + do { \ + if (request != nullptr) { \ + delete request->hdr; \ + /* ats_free(request->hostname_str); */ \ + delete request->api_info; \ + } \ + delete request; \ + delete result; \ + request = new HttpRequestData(); \ + result = new ParentResult(); \ + if (!result || !request) { \ + (void)printf("Allocation failed\n"); \ + return; \ + } \ } while (0) #define ST(x) \ diff --git a/proxy/http/HttpConfig.cc b/proxy/http/HttpConfig.cc index f93765b7284..e2358fb5ace 100644 --- a/proxy/http/HttpConfig.cc +++ b/proxy/http/HttpConfig.cc @@ -35,6 +35,8 @@ #include #include "HttpSessionManager.h" +extern void HostDB_Config_Init(); + #define HttpEstablishStaticConfigStringAlloc(_ix, _n) \ REC_EstablishStaticConfigStringAlloc(_ix, _n); \ REC_RegisterConfigUpdateFunc(_n, http_config_cb, NULL) @@ -1345,7 +1347,7 @@ HttpConfig::startup() HttpEstablishStaticConfigByte(c.referer_filter_enabled, "proxy.config.http.referer_filter"); HttpEstablishStaticConfigByte(c.referer_format_redirect, "proxy.config.http.referer_format_redirect"); - HttpEstablishStaticConfigLongLong(c.oride.down_server_timeout, "proxy.config.http.down_server.cache_time"); + HostDB_Config_Init(); // Negative caching and revalidation HttpEstablishStaticConfigByte(c.oride.negative_caching_enabled, "proxy.config.http.negative_caching_enabled"); diff --git a/proxy/http/HttpConfig.h b/proxy/http/HttpConfig.h index f11e5fa2ad1..ea1e2a86b1b 100644 --- a/proxy/http/HttpConfig.h +++ b/proxy/http/HttpConfig.h @@ -40,6 +40,8 @@ #include #include +#include + #include "tscore/ink_platform.h" #include "tscore/ink_inet.h" #include "tscore/ink_resolver.h" @@ -674,7 +676,7 @@ struct OverridableHttpConfigParams { MgmtInt parent_fail_threshold = 10; MgmtInt per_parent_connect_attempts = 2; - MgmtInt down_server_timeout = 300; + ts_seconds down_server_timeout{300}; MgmtInt client_abort_threshold = 1000; // open read failure retries. diff --git a/proxy/http/HttpConnectionCount.cc b/proxy/http/HttpConnectionCount.cc index 15e6d1a70fa..3d9318f21c1 100644 --- a/proxy/http/HttpConnectionCount.cc +++ b/proxy/http/HttpConnectionCount.cc @@ -31,7 +31,7 @@ using namespace std::literals; -extern int http_config_cb(const char *, RecDataT, RecData, void *); +extern void Enable_Config_Var(std::string_view const &name, bool (*cb)(const char *, RecDataT, RecData, void *), void *cookie); OutboundConnTrack::Imp OutboundConnTrack::_imp; diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc index 36b29e002b7..32494a5cd2f 100644 --- a/proxy/http/HttpSM.cc +++ b/proxy/http/HttpSM.cc @@ -2220,34 +2220,28 @@ HttpSM::state_send_server_request_header(int event, void *data) } void -HttpSM::process_srv_info(HostDBInfo *r) +HttpSM::process_srv_info(HostDBRecord *record) { SMDebug("dns_srv", "beginning process_srv_info"); - t_state.hostdb_entry = Ptr(r); + t_state.dns_info.record = record; /* we didn't get any SRV records, continue w normal lookup */ - if (!r || !r->is_srv || !r->round_robin) { - t_state.dns_info.srv_hostname[0] = '\0'; - t_state.dns_info.srv_lookup_success = false; - t_state.my_txn_conf().srv_enabled = false; + if (!record || !record->is_srv()) { + t_state.dns_info.srv_hostname[0] = '\0'; + t_state.dns_info.resolved_p = false; + t_state.my_txn_conf().srv_enabled = false; SMDebug("dns_srv", "No SRV records were available, continuing to lookup %s", t_state.dns_info.lookup_name); } else { - HostDBRoundRobin *rr = r->rr(); - HostDBInfo *srv = nullptr; - if (rr) { - srv = rr->select_best_srv(t_state.dns_info.srv_hostname, &mutex->thread_holding->generator, ink_local_time(), - static_cast(t_state.txn_conf->down_server_timeout)); - } + HostDBInfo *srv = record->select_best_srv(t_state.dns_info.srv_hostname, &mutex->thread_holding->generator, ts_clock::now(), + t_state.txn_conf->down_server_timeout); if (!srv) { - t_state.dns_info.srv_lookup_success = false; - t_state.dns_info.srv_hostname[0] = '\0'; - t_state.my_txn_conf().srv_enabled = false; + // t_state.dns_info.srv_lookup_success = false; + t_state.dns_info.srv_hostname[0] = '\0'; + t_state.my_txn_conf().srv_enabled = false; SMDebug("dns_srv", "SRV records empty for %s", t_state.dns_info.lookup_name); } else { - t_state.dns_info.srv_lookup_success = true; - t_state.dns_info.srv_port = srv->data.srv.srv_port; - t_state.dns_info.srv_app = srv->app; - // t_state.dns_info.single_srv = (rr->good == 1); + t_state.dns_info.resolved_p = false; + t_state.dns_info.srv_port = srv->data.srv.srv_port; ink_assert(srv->data.srv.key == makeHostHash(t_state.dns_info.srv_hostname)); SMDebug("dns_srv", "select SRV records %s", t_state.dns_info.srv_hostname); } @@ -2256,87 +2250,43 @@ HttpSM::process_srv_info(HostDBInfo *r) } void -HttpSM::process_hostdb_info(HostDBInfo *r) +HttpSM::process_hostdb_info(HostDBRecord *record) { - // Increment the refcount to our item, since we are pointing at it - t_state.hostdb_entry = Ptr(r); + t_state.dns_info.record = record; // protect record. + + bool use_client_addr = t_state.http_config_param->use_client_target_addr == 1 && t_state.client_info.is_transparent && + t_state.dns_info.os_addr_style == ResolveInfo::OS_Addr::TRY_DEFAULT; + + t_state.dns_info.set_active(nullptr); - sockaddr const *client_addr = nullptr; - bool use_client_addr = t_state.http_config_param->use_client_target_addr == 1 && t_state.client_info.is_transparent && - t_state.dns_info.os_addr_style == HttpTransact::DNSLookupInfo::OS_Addr::OS_ADDR_TRY_DEFAULT; if (use_client_addr) { NetVConnection *vc = ua_txn ? ua_txn->get_netvc() : nullptr; if (vc) { - client_addr = vc->get_local_addr(); - // Regardless of whether the client address matches the DNS record or not, - // we want to use that address. Therefore, we copy over the client address - // info and skip the assignment from the DNS cache - ats_ip_copy(t_state.host_db_info.ip(), client_addr); - t_state.dns_info.os_addr_style = HttpTransact::DNSLookupInfo::OS_Addr::OS_ADDR_TRY_CLIENT; - t_state.dns_info.lookup_success = true; - // Leave ret unassigned, so we don't overwrite the host_db_info - } else { - use_client_addr = false; + t_state.dns_info.set_upstream_address(vc->get_local_addr()); + t_state.dns_info.srv_port = ats_ip_port_cast(vc->get_local_addr()); + t_state.dns_info.os_addr_style = ResolveInfo::OS_Addr::TRY_CLIENT; } } - if (r && !r->is_failed()) { - ink_time_t now = ink_local_time(); - HostDBInfo *ret = nullptr; - t_state.dns_info.lookup_success = true; - t_state.dns_info.lookup_validated = true; - - HostDBRoundRobin *rr = r->round_robin ? r->rr() : nullptr; - if (rr) { + if (record && !record->is_failed()) { + t_state.dns_info.inbound_remote_addr = &t_state.client_info.src_addr.sa; + if (!use_client_addr) { + t_state.dns_info.set_active( + record->select_best_http(ts_clock::now(), t_state.dns_info.fail_window, t_state.dns_info.inbound_remote_addr)); + } else { // if use_client_target_addr is set, make sure the client addr is in the results pool - if (use_client_addr && rr->find_ip(client_addr) == nullptr) { + t_state.dns_info.cta_validated_p = true; + if (record->find(t_state.dns_info.addr) == nullptr) { SMDebug("http", "use_client_target_addr == 1. Client specified address is not in the pool, not validated."); - t_state.dns_info.lookup_validated = false; - } else { - // Since the time elapsed between current time and client_request_time - // may be very large, we cannot use client_request_time to approximate - // current time when calling select_best_http(). - ret = rr->select_best_http(&t_state.client_info.src_addr.sa, now, static_cast(t_state.txn_conf->down_server_timeout)); - // set the srv target`s last_failure - if (t_state.dns_info.srv_lookup_success) { - uint32_t last_failure = 0xFFFFFFFF; - for (int i = 0; i < rr->rrcount && last_failure != 0; ++i) { - if (last_failure > rr->info(i).app.http_data.last_failure) { - last_failure = rr->info(i).app.http_data.last_failure; - } - } - - if (last_failure != 0 && static_cast(now - t_state.txn_conf->down_server_timeout) < last_failure) { - HostDBApplicationInfo app; - app.allotment.application1 = 0; - app.allotment.application2 = 0; - app.http_data.last_failure = last_failure; - hostDBProcessor.setby_srv(t_state.dns_info.lookup_name, 0, t_state.dns_info.srv_hostname, &app); - } - } - } - } else { - if (use_client_addr && !ats_ip_addr_eq(client_addr, &r->data.ip.sa)) { - SMDebug("http", "use_client_target_addr == 1. Comparing single addresses failed, not validated."); - t_state.dns_info.lookup_validated = false; - } else { - ret = r; + t_state.dns_info.cta_validated_p = false; } } - if (ret) { - t_state.host_db_info = *ret; - ink_release_assert(!t_state.host_db_info.reverse_dns); - ink_release_assert(ats_is_ip(t_state.host_db_info.ip())); - } } else { SMDebug("http", "[%" PRId64 "] DNS lookup failed for '%s'", sm_id, t_state.dns_info.lookup_name); + } - if (!use_client_addr) { - t_state.dns_info.lookup_success = false; - } - t_state.host_db_info.app.allotment.application1 = 0; - t_state.host_db_info.app.allotment.application2 = 0; - ink_assert(!t_state.host_db_info.round_robin); + if (!t_state.dns_info.resolved_p) { + SMDebug("http", "[%" PRId64 "] resolution failed for '%s'", sm_id, t_state.dns_info.lookup_name); } milestones[TS_MILESTONE_DNS_LOOKUP_END] = Thread::get_hrtime(); @@ -2349,6 +2299,13 @@ HttpSM::process_hostdb_info(HostDBInfo *r) } } +int +HttpSM::state_pre_resolve(int event, void *data) +{ + STATE_ENTER(&HttpSM::state_hostdb_lookup, event); + return 0; +} + ////////////////////////////////////////////////////////////////////////////// // // HttpSM::state_hostdb_lookup() @@ -2367,16 +2324,16 @@ HttpSM::state_hostdb_lookup(int event, void *data) switch (event) { case EVENT_HOST_DB_LOOKUP: pending_action = nullptr; - process_hostdb_info(static_cast(data)); + process_hostdb_info(static_cast(data)); call_transact_and_set_next_state(nullptr); break; case EVENT_SRV_LOOKUP: { pending_action = nullptr; - process_srv_info(static_cast(data)); + process_srv_info(static_cast(data)); - char *host_name = t_state.dns_info.srv_lookup_success ? t_state.dns_info.srv_hostname : t_state.dns_info.lookup_name; + char const *host_name = t_state.dns_info.record->is_srv() ? t_state.dns_info.record->name() : t_state.dns_info.lookup_name; HostDBProcessor::Options opt; - opt.port = t_state.dns_info.srv_lookup_success ? t_state.dns_info.srv_port : t_state.server_info.dst_addr.host_order_port(); + opt.port = t_state.dns_info.record->is_srv() ? t_state.dns_info.srv_port : t_state.server_info.dst_addr.host_order_port(); opt.flags = (t_state.cache_info.directives.does_client_permit_dns_storing) ? HostDBProcessor::HOSTDB_DO_NOT_FORCE_DNS : HostDBProcessor::HOSTDB_FORCE_DNS_RELOAD; opt.timeout = (t_state.api_txn_dns_timeout_value != -1) ? t_state.api_txn_dns_timeout_value : 0; @@ -2411,7 +2368,7 @@ HttpSM::state_hostdb_reverse_lookup(int event, void *data) case EVENT_HOST_DB_LOOKUP: pending_action = nullptr; if (data) { - t_state.request_data.hostname_str = (static_cast(data))->hostname(); + t_state.request_data.hostname_str = (static_cast(data))->name(); } else { SMDebug("http", "[%" PRId64 "] reverse DNS lookup failed for '%s'", sm_id, t_state.dns_info.lookup_name); } @@ -2433,27 +2390,15 @@ int HttpSM::state_mark_os_down(int event, void *data) { STATE_ENTER(&HttpSM::state_mark_os_down, event); - HostDBInfo *mark_down = nullptr; if (event == EVENT_HOST_DB_LOOKUP && data) { - HostDBInfo *r = static_cast(data); - - if (r->round_robin) { - // Look for the entry we need mark down in the round robin - ink_assert(t_state.current.server != nullptr); - ink_assert(t_state.current.request_to == HttpTransact::ORIGIN_SERVER); - if (t_state.current.server) { - mark_down = r->rr()->find_ip(&t_state.current.server->dst_addr.sa); - } - } else { - // No longer a round robin, check to see if our address is the same - if (ats_ip_addr_eq(t_state.host_db_info.ip(), r->ip())) { - mark_down = r; - } - } + auto r = static_cast(data); - if (mark_down) { - mark_host_failure(mark_down, t_state.request_sent_time); + // Look for the entry we need mark down in the round robin + ink_assert(t_state.current.server != nullptr); + ink_assert(t_state.dns_info.looking_up == ResolveInfo::ORIGIN_SERVER); + if (auto *info = r->find(&t_state.dns_info.addr.sa); info != nullptr) { + info->mark_down(ts_clock::now()); } } // We either found our entry or we did not. Either way find @@ -4230,8 +4175,8 @@ HttpSM::do_hostdb_lookup() } pending_action = hostDBProcessor.getSRVbyname_imm(this, (cb_process_result_pfn)&HttpSM::process_srv_info, d, 0, opt); if (pending_action.is_empty()) { - char *host_name = t_state.dns_info.srv_lookup_success ? t_state.dns_info.srv_hostname : t_state.dns_info.lookup_name; - opt.port = t_state.dns_info.srv_lookup_success ? + char const *host_name = t_state.dns_info.resolved_p ? t_state.dns_info.srv_hostname : t_state.dns_info.lookup_name; + opt.port = t_state.dns_info.resolved_p ? t_state.dns_info.srv_port : t_state.server_info.dst_addr.isValid() ? t_state.server_info.dst_addr.host_order_port() : t_state.hdr_info.client_request.port_get(); @@ -4317,18 +4262,10 @@ HttpSM::track_connect_fail() const void HttpSM::do_hostdb_update_if_necessary() { - int issue_update = 0; - - if (t_state.current.server == nullptr || plugin_tunnel_type != HTTP_NO_PLUGIN_TUNNEL) { + if (t_state.current.server == nullptr || plugin_tunnel_type != HTTP_NO_PLUGIN_TUNNEL || t_state.dns_info.active == nullptr) { // No server, so update is not necessary return; } - // If we failed back over to the origin server, we don't have our - // hostdb information anymore which means we shouldn't update the hostdb - if (!ats_ip_addr_eq(&t_state.current.server->dst_addr.sa, t_state.host_db_info.ip())) { - SMDebug("http", "[%" PRId64 "] skipping hostdb update due to server failover", sm_id); - return; - } if (t_state.updated_server_version != HTTP_INVALID) { // we may have incorrectly assumed that the hostdb had the wrong version of @@ -4338,39 +4275,25 @@ HttpSM::do_hostdb_update_if_necessary() // // This test therefore just issues the update only if the hostdb version is // in fact different from the version we want the value to be updated to. - if (t_state.host_db_info.app.http_data.http_version != t_state.updated_server_version) { - t_state.host_db_info.app.http_data.http_version = t_state.updated_server_version; - issue_update |= 1; - } - - t_state.updated_server_version = HTTP_INVALID; + t_state.updated_server_version = HTTP_INVALID; + t_state.dns_info.active->http_version = t_state.updated_server_version; } + // Check to see if we need to report or clear a connection failure if (track_connect_fail()) { - issue_update |= 1; - mark_host_failure(&t_state.host_db_info, t_state.client_request_time); + this->mark_host_failure(&t_state.dns_info, ts_clock::from_time_t(t_state.client_request_time)); } else { - if (t_state.host_db_info.app.http_data.last_failure != 0) { - t_state.host_db_info.app.http_data.last_failure = 0; - t_state.host_db_info.app.http_data.fail_count = 0; - issue_update |= 1; - char addrbuf[INET6_ADDRPORTSTRLEN]; - SMDebug("http", "[%" PRId64 "] hostdb update marking IP: %s as up", sm_id, - ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf))); - } - - if (t_state.dns_info.srv_lookup_success && t_state.dns_info.srv_app.http_data.last_failure != 0) { - t_state.dns_info.srv_app.http_data.last_failure = 0; - hostDBProcessor.setby_srv(t_state.dns_info.lookup_name, 0, t_state.dns_info.srv_hostname, &t_state.dns_info.srv_app); - SMDebug("http", "[%" PRId64 "] hostdb update marking SRV: %s as up", sm_id, t_state.dns_info.srv_hostname); + if (t_state.dns_info.mark_active_server_alive()) { + if (t_state.dns_info.record->is_srv()) { + SMDebug("http", "[%" PRId64 "] hostdb update marking SRV: %s as up", sm_id, t_state.dns_info.record->name()); + } else { + char addrbuf[INET6_ADDRPORTSTRLEN]; + SMDebug("http", "[%" PRId64 "] hostdb update marking IP: %s as up", sm_id, + ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf))); + } } } - if (issue_update) { - hostDBProcessor.setby(t_state.current.server->name, strlen(t_state.current.server->name), &t_state.current.server->dst_addr.sa, - &t_state.host_db_info.app); - } - char addrbuf[INET6_ADDRPORTSTRLEN]; SMDebug("http", "server info = %s", ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf))); return; @@ -4835,7 +4758,7 @@ HttpSM::send_origin_throttled_response() // if the request is to a parent proxy, do not reset // t_state.current.attempts so that another parent or // NextHop may be tried. - if (t_state.current.request_to != HttpTransact::PARENT_PROXY) { + if (t_state.dns_info.looking_up != ResolveInfo::PARENT_PROXY) { t_state.current.attempts = t_state.txn_conf->connect_attempts_max_retries; } t_state.current.state = HttpTransact::OUTBOUND_CONGESTION; @@ -5410,46 +5333,48 @@ HttpSM::do_transform_open() } void -HttpSM::mark_host_failure(HostDBInfo *info, time_t time_down) +HttpSM::mark_host_failure(ResolveInfo *info, ts_time time_down) { char addrbuf[INET6_ADDRPORTSTRLEN]; - if (time_down) { - // Increment the fail_count - ++info->app.http_data.fail_count; - if (info->app.http_data.fail_count >= t_state.txn_conf->connect_attempts_rr_retries) { - if (info->app.http_data.last_failure == 0) { - char *url_str = t_state.hdr_info.client_request.url_string_get(&t_state.arena, nullptr); - int host_len; + if (info->active) { + if (time_down != TS_TIME_ZERO) { + // Increment the fail_count + if (++info->active->fail_count >= t_state.txn_conf->connect_attempts_rr_retries) { + if (info->active) { + if (info->active->last_failure.load() == TS_TIME_ZERO) { + char *url_str = t_state.hdr_info.client_request.url_string_get(&t_state.arena, nullptr); + int host_len; const char *host_name_ptr = t_state.unmapped_url.host_get(&host_len); std::string_view host_name{host_name_ptr, size_t(host_len)}; Log::error("%s", lbw() - .clip(1) - .print("CONNECT: {::s} connecting to {} for host='{}' url='{}' marking down", - ts::bwf::Errno(t_state.current.server->connect_result), t_state.current.server->dst_addr, - host_name, ts::bwf::FirstOf(url_str, "")) - .extend(1) - .write('\0') - .data()); - - if (url_str) { - t_state.arena.str_free(url_str); + .clip(1) + .print("CONNECT : {::s} connecting to {} for host='{}' url='{}' marking down", + ts::bwf::Errno(t_state.current.server->connect_result), t_state.current.server->dst_addr, + host_name,ts::bwf::FirstOf(url_str, "")) + .extend(1) + .write('\0') + .data()); + + if (url_str) { + t_state.arena.str_free(url_str); + } + } + info->active->last_failure = time_down; + SMDebug("http", "[%" PRId64 "] hostdb update marking IP: %s as down", sm_id, + ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf))); + } else { + SMDebug("http", "[%" PRId64 "] hostdb increment IP failcount %s to %d", sm_id, + ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)), info->active->fail_count.load()); } + } else { // Clear the failure + info->active->fail_count = 0; + info->active->last_failure = time_down; } - info->app.http_data.last_failure = time_down; - SMDebug("http", "[%" PRId64 "] hostdb update marking IP: %s as down", sm_id, - ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf))); - } else { - SMDebug("http", "[%" PRId64 "] hostdb increment IP failcount %s to %d", sm_id, - ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)), info->app.http_data.fail_count); } - } else { // Clear the failure - info->app.http_data.fail_count = 0; - info->app.http_data.last_failure = time_down; } - #ifdef DEBUG - ink_assert(ink_local_time() + t_state.txn_conf->down_server_timeout > time_down); + ink_assert(std::chrono::system_clock::now() + t_state.txn_conf->down_server_timeout > time_down); #endif } @@ -5722,6 +5647,7 @@ HttpSM::handle_server_setup_error(int event, void *data) } } + [[maybe_unused]] UnixNetVConnection *dbg_vc = nullptr; switch (event) { case VC_EVENT_EOS: t_state.current.state = HttpTransact::CONNECTION_CLOSED; @@ -7446,65 +7372,48 @@ HttpSM::set_next_state() } case HttpTransact::SM_ACTION_DNS_LOOKUP: { - sockaddr const *addr; - - if (t_state.api_server_addr_set) { - /* If the API has set the server address before the OS DNS lookup - * then we can skip the lookup - */ - ip_text_buffer ipb; - SMDebug("dns", "[HttpTransact::HandleRequest] Skipping DNS lookup for API supplied target %s.", - ats_ip_ntop(&t_state.server_info.dst_addr, ipb, sizeof(ipb))); - // this seems wasteful as we will just copy it right back - ats_ip_copy(t_state.host_db_info.ip(), &t_state.server_info.dst_addr); - t_state.dns_info.lookup_success = true; - call_transact_and_set_next_state(nullptr); - break; - } else if (0 == ats_ip_pton(t_state.dns_info.lookup_name, t_state.host_db_info.ip()) && - ats_is_ip_loopback(t_state.host_db_info.ip())) { - // If it's 127.0.0.1 or ::1 don't bother with hostdb - SMDebug("dns", "[HttpTransact::HandleRequest] Skipping DNS lookup for %s because it's loopback", - t_state.dns_info.lookup_name); - t_state.dns_info.lookup_success = true; - call_transact_and_set_next_state(nullptr); - break; - } else if (t_state.http_config_param->use_client_target_addr == 2 && !t_state.url_remap_success && - t_state.parent_result.result != PARENT_SPECIFIED && t_state.client_info.is_transparent && - t_state.dns_info.os_addr_style == HttpTransact::DNSLookupInfo::OS_Addr::OS_ADDR_TRY_DEFAULT && - ats_is_ip(addr = ua_txn->get_netvc()->get_local_addr())) { - /* If the connection is client side transparent and the URL - * was not remapped/directed to parent proxy, we can use the - * client destination IP address instead of doing a DNS - * lookup. This is controlled by the 'use_client_target_addr' - * configuration parameter. + if (sockaddr const *addr; t_state.http_config_param->use_client_target_addr == 2 && // no CTA verification + !t_state.url_remap_success && // wasn't remapped + t_state.parent_result.result != PARENT_SPECIFIED && // no parent. + t_state.client_info.is_transparent && // inbound transparent + t_state.dns_info.os_addr_style == ResolveInfo::OS_Addr::TRY_DEFAULT && // haven't tried anything yet. + ats_is_ip(addr = ua_txn->get_netvc()->get_local_addr())) // valid inbound remote address + { + /* If the connection is client side transparent and the URL was not remapped/directed to + * parent proxy, we can use the client destination IP address instead of doing a DNS lookup. + * This is controlled by the 'use_client_target_addr' configuration parameter. */ if (is_debug_tag_set("dns")) { ip_text_buffer ipb; SMDebug("dns", "[HttpTransact::HandleRequest] Skipping DNS lookup for client supplied target %s.", ats_ip_ntop(addr, ipb, sizeof(ipb))); } - ats_ip_copy(t_state.host_db_info.ip(), addr); - t_state.host_db_info.app.http_data.http_version = t_state.hdr_info.client_request.version_get(); - t_state.dns_info.lookup_success = true; - // cache this result so we don't have to unreliably duplicate the - // logic later if the connect fails. - t_state.dns_info.os_addr_style = HttpTransact::DNSLookupInfo::OS_Addr::OS_ADDR_TRY_CLIENT; + t_state.dns_info.set_upstream_address(addr); + + // Make a note the CTA is being used - don't do this case again. + t_state.dns_info.os_addr_style = ResolveInfo::OS_Addr::TRY_CLIENT; + + if (t_state.hdr_info.client_request.version_get() == HTTPVersion(0, 9)) { + t_state.dns_info.http_version = HTTP_0_9; + } else if (t_state.hdr_info.client_request.version_get() == HTTPVersion(1, 0)) { + t_state.dns_info.http_version = HTTP_1_0; + } else { + t_state.dns_info.http_version = HTTP_1_1; + } + call_transact_and_set_next_state(nullptr); break; - } else if (t_state.parent_result.result == PARENT_UNDEFINED && t_state.dns_info.lookup_success) { - // Already set, and we don't have a parent proxy to lookup - ink_assert(ats_is_ip(t_state.host_db_info.ip())); - SMDebug("dns", "[HttpTransact::HandleRequest] Skipping DNS lookup, provided by plugin"); + } else if (t_state.dns_info.looking_up == ResolveInfo::ORIGIN_SERVER && t_state.http_config_param->no_dns_forward_to_parent && + t_state.parent_result.result != PARENT_UNDEFINED) { + t_state.dns_info.resolved_p = true; // seems dangerous - where's the IP address? call_transact_and_set_next_state(nullptr); break; - } else if (t_state.dns_info.looking_up == HttpTransact::ORIGIN_SERVER && t_state.http_config_param->no_dns_forward_to_parent && - t_state.parent_result.result != PARENT_UNDEFINED) { - t_state.dns_info.lookup_success = true; + } else if (t_state.dns_info.resolve_immediate()) { call_transact_and_set_next_state(nullptr); break; } - + // else have to do DNS. HTTP_SM_SET_DEFAULT_HANDLER(&HttpSM::state_hostdb_lookup); // We need to close the previous attempt @@ -7532,7 +7441,7 @@ HttpSM::set_next_state() } } - ink_assert(t_state.dns_info.looking_up != HttpTransact::UNDEFINED_LOOKUP); + ink_assert(t_state.dns_info.looking_up != ResolveInfo::UNDEFINED_LOOKUP); do_hostdb_lookup(); break; } @@ -7715,7 +7624,7 @@ HttpSM::set_next_state() case HttpTransact::SM_ACTION_ORIGIN_SERVER_RR_MARK_DOWN: { HTTP_SM_SET_DEFAULT_HANDLER(&HttpSM::state_mark_os_down); - ink_assert(t_state.dns_info.looking_up == HttpTransact::ORIGIN_SERVER); + ink_assert(t_state.dns_info.looking_up == ResolveInfo::ORIGIN_SERVER); // TODO: This might not be optimal (or perhaps even correct), but it will // effectively mark the host as down. What's odd is that state_mark_os_down @@ -7983,13 +7892,13 @@ HttpSM::redirect_request(const char *arg_redirect_url, const int arg_redirect_le t_state.response_received_time = 0; t_state.next_action = HttpTransact::SM_ACTION_REDIRECT_READ; // we have a new OS and need to have DNS lookup the new OS - t_state.dns_info.lookup_success = false; - t_state.force_dns = false; + t_state.dns_info.resolved_p = false; + t_state.force_dns = false; t_state.server_info.clear(); t_state.parent_info.clear(); // Must reset whether the InkAPI has set the destination address - t_state.api_server_addr_set = false; + // t_state.dns_info.api_addr_set_p = false; if (t_state.txn_conf->cache_http) { t_state.cache_info.object_read = nullptr; diff --git a/proxy/http/HttpSM.h b/proxy/http/HttpSM.h index 9bff26e04c0..34fb7c71cd3 100644 --- a/proxy/http/HttpSM.h +++ b/proxy/http/HttpSM.h @@ -314,8 +314,8 @@ class HttpSM : public Continuation, public PluginUserArgs // Handles the setting of all state necessary before // calling transact to process the hostdb lookup // A NULL 'r' argument indicates the hostdb lookup failed - void process_hostdb_info(HostDBInfo *r); - void process_srv_info(HostDBInfo *r); + void process_hostdb_info(HostDBRecord *record); + void process_srv_info(HostDBRecord *record); // Called by transact. Synchronous. VConnection *do_transform_open(); @@ -465,6 +465,7 @@ class HttpSM : public Continuation, public PluginUserArgs int state_read_client_request_header(int event, void *data); int state_watch_for_client_abort(int event, void *data); int state_read_push_response_header(int event, void *data); + int state_pre_resolve(int event, void *data); int state_hostdb_lookup(int event, void *data); int state_hostdb_reverse_lookup(int event, void *data); int state_mark_os_down(int event, void *data); @@ -532,7 +533,7 @@ class HttpSM : public Continuation, public PluginUserArgs void handle_server_setup_error(int event, void *data); void handle_http_server_open(); void handle_post_failure(); - void mark_host_failure(HostDBInfo *info, time_t time_down); + void mark_host_failure(ResolveInfo *info, ts_time time_down); void release_server_session(bool serve_from_cache = false); void set_ua_abort(HttpTransact::AbortState_t ua_abort, int event); int write_header_into_buffer(HTTPHdr *h, MIOBuffer *b); diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc index 47092a3353a..63784cc63ad 100644 --- a/proxy/http/HttpTransact.cc +++ b/proxy/http/HttpTransact.cc @@ -372,7 +372,7 @@ HttpTransact::is_response_valid(State *s, HTTPHdr *incoming_response) inline static ParentRetry_t response_is_retryable(HttpTransact::State *s, HTTPStatus response_code) { - if (!HttpTransact::is_response_valid(s, &s->hdr_info.server_response) || s->current.request_to != HttpTransact::PARENT_PROXY) { + if (!HttpTransact::is_response_valid(s, &s->hdr_info.server_response) || s->current.request_to != ResolveInfo::PARENT_PROXY) { return PARENT_RETRY_NONE; } if (s->response_action.handled) { @@ -487,8 +487,8 @@ update_cache_control_information_from_config(HttpTransact::State *s) bool HttpTransact::is_server_negative_cached(State *s) { - if (s->host_db_info.app.http_data.last_failure != 0 && - s->host_db_info.app.http_data.last_failure + s->txn_conf->down_server_timeout > s->client_request_time) { + if (s->dns_info.active && s->dns_info.active->last_fail_time() != TS_TIME_ZERO && + s->dns_info.active->last_fail_time() + s->txn_conf->down_server_timeout > ts_clock::from_time_t(s->client_request_time)) { return true; } else { // Make sure some nasty clock skew has not happened @@ -496,9 +496,10 @@ HttpTransact::is_server_negative_cached(State *s) // future we should tolerate bogus last failure times. This sets // the upper bound to the time that we would ever consider a server // down to 2*down_server_timeout - if (s->client_request_time + s->txn_conf->down_server_timeout < s->host_db_info.app.http_data.last_failure) { - s->host_db_info.app.http_data.last_failure = 0; - s->host_db_info.app.http_data.fail_count = 0; + if (s->dns_info.active && + ts_clock::from_time_t(s->client_request_time) + s->txn_conf->down_server_timeout < s->dns_info.active->last_fail_time()) { + s->dns_info.active->last_failure = TS_TIME_ZERO; + s->dns_info.active->fail_count = 0; ink_assert(!"extreme clock skew"); return true; } @@ -507,8 +508,8 @@ HttpTransact::is_server_negative_cached(State *s) } inline static void -update_current_info(HttpTransact::CurrentInfo *into, HttpTransact::ConnectionAttributes *from, HttpTransact::LookingUp_t who, - int attempts) +update_current_info(HttpTransact::CurrentInfo *into, HttpTransact::ConnectionAttributes *from, + ResolveInfo::UpstreamResolveStyle who, int attempts) { into->request_to = who; into->server = from; @@ -516,7 +517,7 @@ update_current_info(HttpTransact::CurrentInfo *into, HttpTransact::ConnectionAtt } inline static void -update_dns_info(HttpTransact::DNSLookupInfo *dns, HttpTransact::CurrentInfo *from) +update_dns_info(ResolveInfo *dns, HttpTransact::CurrentInfo *from) { dns->looking_up = from->request_to; dns->lookup_name = from->server->name; @@ -558,7 +559,7 @@ is_negative_caching_appropriate(HttpTransact::State *s) } } -inline static HttpTransact::LookingUp_t +inline static ResolveInfo::UpstreamResolveStyle find_server_and_update_current_info(HttpTransact::State *s) { int host_len; @@ -639,16 +640,16 @@ find_server_and_update_current_info(HttpTransact::State *s) switch (s->parent_result.result) { case PARENT_SPECIFIED: s->parent_info.name = s->arena.str_store(s->parent_result.hostname, strlen(s->parent_result.hostname)); - update_current_info(&s->current, &s->parent_info, HttpTransact::PARENT_PROXY, (s->current.attempts)++); + update_current_info(&s->current, &s->parent_info, ResolveInfo::PARENT_PROXY, (s->current.attempts)++); update_dns_info(&s->dns_info, &s->current); - ink_assert(s->dns_info.looking_up == HttpTransact::PARENT_PROXY); + ink_assert(s->dns_info.looking_up == ResolveInfo::PARENT_PROXY); s->next_hop_scheme = URL_WKSIDX_HTTP; - return HttpTransact::PARENT_PROXY; + return ResolveInfo::PARENT_PROXY; case PARENT_FAIL: // No more parents - need to return an error message - s->current.request_to = HttpTransact::HOST_NONE; - return HttpTransact::HOST_NONE; + s->current.request_to = ResolveInfo::HOST_NONE; + return ResolveInfo::HOST_NONE; case PARENT_DIRECT: // if the configuration does not allow the origin to be dns'd @@ -656,15 +657,15 @@ find_server_and_update_current_info(HttpTransact::State *s) if (s->http_config_param->no_dns_forward_to_parent) { Warning("no available parents and the config proxy.config.http.no_dns_just_forward_to_parent, prevents origin lookups."); s->parent_result.result = PARENT_FAIL; - return HttpTransact::HOST_NONE; + return ResolveInfo::HOST_NONE; } /* fall through */ default: - update_current_info(&s->current, &s->server_info, HttpTransact::ORIGIN_SERVER, (s->current.attempts)++); + update_current_info(&s->current, &s->server_info, ResolveInfo::ORIGIN_SERVER, (s->current.attempts)++); update_dns_info(&s->dns_info, &s->current); - ink_assert(s->dns_info.looking_up == HttpTransact::ORIGIN_SERVER); + ink_assert(s->dns_info.looking_up == ResolveInfo::ORIGIN_SERVER); s->next_hop_scheme = s->scheme; - return HttpTransact::ORIGIN_SERVER; + return ResolveInfo::ORIGIN_SERVER; } } @@ -764,7 +765,7 @@ does_method_effect_cache(int method) inline static HttpTransact::StateMachineAction_t how_to_open_connection(HttpTransact::State *s) { - ink_assert((s->pending_work == nullptr) || (s->current.request_to == HttpTransact::PARENT_PROXY)); + ink_assert((s->pending_work == nullptr) || (s->current.request_to == ResolveInfo::PARENT_PROXY)); // Originally we returned which type of server to open // Now, however, we may want to issue a cache @@ -1685,13 +1686,13 @@ HttpTransact::setup_plugin_request_intercept(State *s) s->scheme = s->next_hop_scheme = URL_WKSIDX_HTTP; // Set up a "fake" server server entry - update_current_info(&s->current, &s->server_info, HttpTransact::ORIGIN_SERVER, 0); + update_current_info(&s->current, &s->server_info, ResolveInfo::ORIGIN_SERVER, 0); // Also "fake" the info we'd normally get from // hostDB - s->server_info.http_version = HTTP_1_0; - s->server_info.keep_alive = HTTP_NO_KEEPALIVE; - s->host_db_info.app.http_data.http_version = HTTP_1_0; + s->server_info.http_version = HTTP_1_0; + s->server_info.keep_alive = HTTP_NO_KEEPALIVE; + s->server_info.http_version = HTTP_1_0; s->server_info.dst_addr.setToAnyAddr(AF_INET); // must set an address or we can't set the port. s->server_info.dst_addr.port() = htons(s->hdr_info.client_request.port_get()); // this is the info that matters. @@ -1783,19 +1784,19 @@ HttpTransact::PPDNSLookup(State *s) { TxnDebug("http_trans", "[HttpTransact::PPDNSLookup]"); - ink_assert(s->dns_info.looking_up == PARENT_PROXY); - if (!s->dns_info.lookup_success) { + ink_assert(s->dns_info.looking_up == ResolveInfo::PARENT_PROXY); + if (!s->dns_info.resolved_p) { // Mark parent as down due to resolving failure markParentDown(s); // DNS lookup of parent failed, find next parent or o.s. - if (find_server_and_update_current_info(s) == HttpTransact::HOST_NONE) { - ink_assert(s->current.request_to == HOST_NONE); + if (find_server_and_update_current_info(s) == ResolveInfo::HOST_NONE) { + ink_assert(s->current.request_to == ResolveInfo::HOST_NONE); handle_parent_died(s); return; } if (!s->current.server->dst_addr.isValid()) { - if (s->current.request_to == PARENT_PROXY) { + if (s->current.request_to == ResolveInfo::PARENT_PROXY) { if (!s->response_action.handled) { TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup); } else { @@ -1807,16 +1808,16 @@ HttpTransact::PPDNSLookup(State *s) return; } else { // We could be out of parents here if all the parents failed DNS lookup - ink_assert(s->current.request_to == HOST_NONE); + ink_assert(s->current.request_to == ResolveInfo::HOST_NONE); handle_parent_died(s); } return; } } else { // lookup succeeded, open connection to p.p. - ats_ip_copy(&s->parent_info.dst_addr, s->host_db_info.ip()); + // ats_ip_copy(&s->parent_info.dst_addr, s->host_db_info.ip()); s->parent_info.dst_addr.port() = htons(s->parent_result.port); - get_ka_info_from_host_db(s, &s->parent_info, &s->client_info, &s->host_db_info); + get_ka_info_from_host_db(s, &s->parent_info, &s->client_info, s->dns_info.active); char addrbuf[INET6_ADDRSTRLEN]; TxnDebug("http_trans", "[PPDNSLookup] DNS lookup for sm_id[%" PRId64 "] successful IP: %s", s->state_machine->sm_id, @@ -1840,65 +1841,6 @@ HttpTransact::PPDNSLookup(State *s) s->next_action = how_to_open_connection(s); } -/////////////////////////////////////////////////////////////////////////////// -// -// Name : ReDNSRoundRobin -// Description: Called after we fail to contact part of a round-robin -// robin server set and we found a another ip address. -// -// Details : -// -// -// -// Possible Next States From Here: -// - HttpTransact::ORIGIN_SERVER_RAW_OPEN; -// - HttpTransact::ORIGIN_SERVER_OPEN; -// - HttpTransact::PROXY_INTERNAL_CACHE_NOOP; -// -/////////////////////////////////////////////////////////////////////////////// -void -HttpTransact::ReDNSRoundRobin(State *s) -{ - ink_assert(s->current.server == &s->server_info); - ink_assert(s->current.server->had_connect_fail()); - - if (s->dns_info.lookup_success) { - // We using a new server now so clear the connection - // failure mark - s->current.server->clear_connect_fail(); - - // Our ReDNS of the server succeeded so update the necessary - // information and try again. Need to preserve the current port value if possible. - in_port_t server_port = s->current.server->dst_addr.host_order_port(); - // Temporary check to make sure the port preservation can be depended upon. That should be the case - // because we get here only after trying a connection. Remove for 6.2. - ink_assert(s->current.server->dst_addr.isValid() && 0 != server_port); - - ats_ip_copy(&s->server_info.dst_addr, s->host_db_info.ip()); - s->server_info.dst_addr.port() = htons(server_port); - ats_ip_copy(&s->request_data.dest_ip, &s->server_info.dst_addr); - get_ka_info_from_host_db(s, &s->server_info, &s->client_info, &s->host_db_info); - - char addrbuf[INET6_ADDRSTRLEN]; - TxnDebug("http_trans", "[ReDNSRoundRobin] DNS lookup for O.S. successful IP: %s", - ats_ip_ntop(&s->server_info.dst_addr.sa, addrbuf, sizeof(addrbuf))); - - s->next_action = how_to_open_connection(s); - } else { - // Our ReDNS failed so output the DNS failure error message - // Set to internal server error so later logging will pick up SQUID_LOG_ERR_DNS_FAIL - build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Cannot find server.", "connect#dns_failed"); - s->cache_info.action = CACHE_DO_NO_ACTION; - s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP; - // s->next_action = PROXY_INTERNAL_CACHE_NOOP; - char *url_str = s->hdr_info.client_request.url_string_get(&s->arena, nullptr); - Log::error("%s", - lbw().clip(1).print("DNS Error: looking up {}", ts::bwf::FirstOf(url_str, "")).extend(1).write('\0').data()); - } - - return; -} - /////////////////////////////////////////////////////////////////////////////// // Name : OSDNSLookup // Description: called after the DNS lookup of origin server name @@ -1928,37 +1870,36 @@ HttpTransact::ReDNSRoundRobin(State *s) void HttpTransact::OSDNSLookup(State *s) { - ink_assert(s->dns_info.looking_up == ORIGIN_SERVER); + ink_assert(s->dns_info.looking_up == ResolveInfo::UpstreamResolveStyle::ORIGIN_SERVER); TxnDebug("http_trans", "[HttpTransact::OSDNSLookup]"); - // It's never valid to connect *to* INADDR_ANY, so let's reject the request now. - if (ats_is_ip_any(s->host_db_info.ip())) { - TxnDebug("http_trans", "[OSDNSLookup] Invalid request IP: INADDR_ANY"); - build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Bad Destination Address", "request#syntax_error"); - SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD); - TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr); - } - - if (!s->dns_info.lookup_success) { - if (DNSLookupInfo::OS_Addr::OS_ADDR_TRY_HOSTDB == s->dns_info.os_addr_style) { - /* - * Transparent case: We tried to connect to client target address, failed and tried to use a different addr - * No HostDB data, just keep on with the CTA. + if (!s->dns_info.resolved_p) { + if (ResolveInfo::OS_Addr::TRY_HOSTDB == s->dns_info.os_addr_style) { + /* Transparent case: We tried to connect to client target address, failed and tried to use a different addr + * but that failed to resolve therefore keep on with the CTA. */ - s->dns_info.lookup_success = true; - s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT; + s->dns_info.addr.assign(s->state_machine->ua_txn->get_netvc()->get_local_addr()); // fetch CTA + s->dns_info.resolved_p = true; + s->dns_info.os_addr_style = ResolveInfo::OS_Addr::USE_CLIENT; TxnDebug("http_seq", "[HttpTransact::OSDNSLookup] DNS lookup unsuccessful, using client target address"); } else { TxnDebug("http_seq", "[HttpTransact::OSDNSLookup] DNS Lookup unsuccessful"); + char const *log_msg; // output the DNS failure error message SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD); - // Set to internal server error so later logging will pick up SQUID_LOG_ERR_DNS_FAIL - build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Cannot find server.", "connect#dns_failed"); + if (!s->dns_info.record || s->dns_info.record->is_failed()) { + // Set to internal server error so later logging will pick up SQUID_LOG_ERR_DNS_FAIL + build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Cannot find server.", "connect#dns_failed"); + log_msg = "looking up"; + } else { + build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "No valid server.", "connect#all_dead"); + log_msg = "no valid server"; + } char *url_str = s->hdr_info.client_request.url_string_get(&s->arena, nullptr); - Log::error("%s", - lbw().clip(1).print("DNS Error: looking up {}", ts::bwf::FirstOf(url_str, "")).extend(1).write('\0').data()); + Log::error( + "%s", lbw().clip(1).print("DNS Error: {} {}", log_msg, ts::bwf::FirstOf(url_str, "")).extend(1).write('\0').data()); // s->cache_info.action = CACHE_DO_NO_ACTION; TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr); } @@ -1966,43 +1907,41 @@ HttpTransact::OSDNSLookup(State *s) } // The dns lookup succeeded - ink_assert(s->dns_info.lookup_success); + ink_assert(s->dns_info.resolved_p); TxnDebug("http_seq", "[HttpTransact::OSDNSLookup] DNS Lookup successful"); + // It's never valid to connect *to* INADDR_ANY, so let's reject the request now. + if (ats_is_ip_any(s->dns_info.addr)) { + TxnDebug("http_trans", "[OSDNSLookup] Invalid request IP: INADDR_ANY"); + build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Bad Destination Address", "request#syntax_error"); + SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD); + TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr); + } + // For the transparent case, nail down the kind of address we are really using - if (DNSLookupInfo::OS_Addr::OS_ADDR_TRY_HOSTDB == s->dns_info.os_addr_style) { + if (ResolveInfo::OS_Addr::TRY_HOSTDB == s->dns_info.os_addr_style) { // We've backed off from a client supplied address and found some // HostDB addresses. We use those if they're different from the CTA. // In all cases we now commit to client or HostDB for our source. - if (s->host_db_info.round_robin) { - HostDBInfo *cta = s->host_db_info.rr()->select_next(&s->current.server->dst_addr.sa); - if (cta) { - // found another addr, lock in host DB. - s->host_db_info = *cta; - s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_HOSTDB; - } else { - // nothing else there, continue with CTA. - s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT; - } - } else if (ats_ip_addr_eq(s->host_db_info.ip(), &s->server_info.dst_addr.sa)) { - s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT; + if (s->dns_info.set_active(&s->current.server->dst_addr.sa) && s->dns_info.select_next_rr()) { + s->dns_info.os_addr_style = ResolveInfo::OS_Addr::USE_HOSTDB; } else { - s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_HOSTDB; + // nothing else there, continue with CTA. + s->dns_info.set_active(nullptr); + s->dns_info.set_upstream_address(&s->current.server->dst_addr.sa); + s->dns_info.os_addr_style = ResolveInfo::OS_Addr::USE_CLIENT; } } - // Check to see if can fullfill expect requests based on the cached - // update some state variables with hostdb information that has - // been provided. - ats_ip_copy(&s->server_info.dst_addr, s->host_db_info.ip()); + s->server_info.dst_addr.assign(s->dns_info.addr); // If the SRV response has a port number, we should honor it. Otherwise we do the port defined in remap - if (s->dns_info.srv_lookup_success) { + if (s->dns_info.resolved_p && s->dns_info.srv_port) { s->server_info.dst_addr.port() = htons(s->dns_info.srv_port); - } else if (!s->api_server_addr_set) { + } else { s->server_info.dst_addr.port() = htons(s->hdr_info.client_request.port_get()); // now we can set the port. } ats_ip_copy(&s->request_data.dest_ip, &s->server_info.dst_addr); - get_ka_info_from_host_db(s, &s->server_info, &s->client_info, &s->host_db_info); + get_ka_info_from_host_db(s, &s->server_info, &s->client_info, s->dns_info.active); char addrbuf[INET6_ADDRSTRLEN]; TxnDebug("http_trans", @@ -2013,14 +1952,17 @@ HttpTransact::OSDNSLookup(State *s) if (s->redirect_info.redirect_in_process) { // If dns lookup was not successful, the code below will handle the error. RedirectEnabled::Action action = RedirectEnabled::Action::INVALID; - if (true == Machine::instance()->is_self(s->host_db_info.ip())) { + if (true == Machine::instance()->is_self(&s->dns_info.addr.sa)) { action = s->http_config_param->redirect_actions_self_action; + TxnDebug("http_trans", "[OSDNSLookup] Self action - %d.", int(action)); } else { // Make sure the return value from contains is big enough for a void*. intptr_t x{intptr_t(RedirectEnabled::Action::INVALID)}; ink_release_assert(s->http_config_param->redirect_actions_map != nullptr); - ink_release_assert(s->http_config_param->redirect_actions_map->contains(s->host_db_info.ip(), reinterpret_cast(&x))); + ink_release_assert(s->http_config_param->redirect_actions_map->contains(s->dns_info.addr, reinterpret_cast(&x))); action = static_cast(x); + TxnDebug("http_trans", "[OSDNSLookup] Mapped action - %d for family %d.", int(action), + int(s->dns_info.active->data.ip.family())); } if (action == RedirectEnabled::Action::FOLLOW) { @@ -2055,8 +1997,8 @@ HttpTransact::OSDNSLookup(State *s) // After SM_ACTION_DNS_LOOKUP, goto the saved action/state ORIGIN_SERVER_(RAW_)OPEN. // Should we skip the StartAccessControl()? why? - if (DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT == s->dns_info.os_addr_style || - DNSLookupInfo::OS_Addr::OS_ADDR_USE_HOSTDB == s->dns_info.os_addr_style) { + if (ResolveInfo::OS_Addr::USE_CLIENT == s->dns_info.os_addr_style || + ResolveInfo::OS_Addr::USE_HOSTDB == s->dns_info.os_addr_style) { // we've come back after already trying the server to get a better address // and finished with all backtracking - return to trying the server. TRANSACT_RETURN(how_to_open_connection(s), HttpTransact::HandleResponse); @@ -2225,14 +2167,14 @@ HttpTransact::LookupSkipOpenServer(State *s) // to a parent proxy or to the origin server. find_server_and_update_current_info(s); - if (s->current.request_to == PARENT_PROXY) { + if (s->current.request_to == ResolveInfo::PARENT_PROXY) { TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup); } else if (s->parent_result.result == PARENT_FAIL) { handle_parent_died(s); return; } - ink_assert(s->current.request_to == ORIGIN_SERVER); + ink_assert(s->current.request_to == ResolveInfo::ORIGIN_SERVER); // ink_assert(s->current.server->ip != 0); build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version); @@ -2902,18 +2844,18 @@ HttpTransact::HandleCacheOpenReadHit(State *s) // scheme & 2) If we skip down parents, every page // we serve is potentially stale // - if (s->current.request_to == ORIGIN_SERVER && is_server_negative_cached(s) && response_returnable == true && + if (s->current.request_to == ResolveInfo::ORIGIN_SERVER && is_server_negative_cached(s) && response_returnable == true && is_stale_cache_response_returnable(s) == true) { server_up = false; - update_current_info(&s->current, nullptr, UNDEFINED_LOOKUP, 0); + update_current_info(&s->current, nullptr, ResolveInfo::UNDEFINED_LOOKUP, 0); TxnDebug("http_trans", "CacheOpenReadHit - server_down, returning stale document"); } // a parent lookup could come back as PARENT_FAIL if in parent.config, go_direct == false and // there are no available parents (all down). - else if (s->current.request_to == HOST_NONE && s->parent_result.result == PARENT_FAIL) { + else if (s->current.request_to == ResolveInfo::HOST_NONE && s->parent_result.result == PARENT_FAIL) { if (is_server_negative_cached(s) && response_returnable == true && is_stale_cache_response_returnable(s) == true) { server_up = false; - update_current_info(&s->current, nullptr, UNDEFINED_LOOKUP, 0); + update_current_info(&s->current, nullptr, ResolveInfo::UNDEFINED_LOOKUP, 0); TxnDebug("http_trans", "CacheOpenReadHit - server_down, returning stale document"); } else { handle_parent_died(s); @@ -2936,14 +2878,14 @@ HttpTransact::HandleCacheOpenReadHit(State *s) // through. The request will fail because of the // missing ip but we won't take down the system // - if (s->current.request_to == PARENT_PROXY) { + if (s->current.request_to == ResolveInfo::PARENT_PROXY) { // Set ourselves up to handle pending revalidate issues // after the PP DNS lookup ink_assert(s->pending_work == nullptr); s->pending_work = issue_revalidate; TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup); - } else if (s->current.request_to == ORIGIN_SERVER) { + } else if (s->current.request_to == ResolveInfo::ORIGIN_SERVER) { return CallOSDNSLookup(s); } else { handle_parent_died(s); @@ -3364,12 +3306,12 @@ HttpTransact::HandleCacheOpenReadMiss(State *s) return; } if (!s->current.server->dst_addr.isValid()) { - ink_release_assert(s->parent_result.result == PARENT_DIRECT || s->current.request_to == PARENT_PROXY || + ink_release_assert(s->parent_result.result == PARENT_DIRECT || s->current.request_to == ResolveInfo::PARENT_PROXY || s->http_config_param->no_dns_forward_to_parent != 0); if (s->parent_result.result == PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 1) { return CallOSDNSLookup(s); } - if (s->current.request_to == PARENT_PROXY) { + if (s->current.request_to == ResolveInfo::PARENT_PROXY) { TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, HttpTransact::PPDNSLookup); } else { handle_parent_died(s); @@ -3478,7 +3420,7 @@ HttpTransact::HandleResponse(State *s) HTTP_INCREMENT_DYN_STAT(http_incoming_responses_stat); - ink_release_assert(s->current.request_to != UNDEFINED_LOOKUP); + ink_release_assert(s->current.request_to != ResolveInfo::UNDEFINED_LOOKUP); if (s->cache_info.action != CACHE_DO_WRITE) { ink_release_assert(s->cache_info.action != CACHE_DO_LOOKUP); ink_release_assert(s->cache_info.action != CACHE_DO_SERVE); @@ -3495,10 +3437,10 @@ HttpTransact::HandleResponse(State *s) } switch (s->current.request_to) { - case PARENT_PROXY: + case ResolveInfo::PARENT_PROXY: handle_response_from_parent(s); break; - case ORIGIN_SERVER: + case ResolveInfo::ORIGIN_SERVER: handle_response_from_server(s); break; default: @@ -3622,7 +3564,7 @@ HttpTransact::HandleStatPage(State *s) void HttpTransact::handle_response_from_parent(State *s) { - LookingUp_t next_lookup = UNDEFINED_LOOKUP; + auto next_lookup = ResolveInfo::UNDEFINED_LOOKUP; TxnDebug("http_trans", "[handle_response_from_parent] (hrfp)"); HTTP_RELEASE_ASSERT(s->current.server == &s->parent_info); @@ -3716,7 +3658,7 @@ HttpTransact::handle_response_from_parent(State *s) markParentDown(s); } s->parent_result.result = PARENT_FAIL; - next_lookup = HOST_NONE; + next_lookup = ResolveInfo::HOST_NONE; } break; } @@ -3724,15 +3666,15 @@ HttpTransact::handle_response_from_parent(State *s) // We have either tried to find a new parent or failed over to the // origin server switch (next_lookup) { - case PARENT_PROXY: - ink_assert(s->current.request_to == PARENT_PROXY); + case ResolveInfo::PARENT_PROXY: + ink_assert(s->current.request_to == ResolveInfo::PARENT_PROXY); TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup); break; - case ORIGIN_SERVER: + case ResolveInfo::ORIGIN_SERVER: // Next lookup is Origin Server, try DNS for Origin Server return CallOSDNSLookup(s); break; - case HOST_NONE: + case ResolveInfo::HOST_NONE: handle_parent_died(s); break; default: @@ -3806,38 +3748,25 @@ HttpTransact::handle_response_from_server(State *s) if (is_request_retryable(s) && s->current.attempts < max_connect_retries) { // If this is a round robin DNS entry & we're tried configured // number of times, we should try another node - if (DNSLookupInfo::OS_Addr::OS_ADDR_TRY_CLIENT == s->dns_info.os_addr_style) { - // attempt was based on client supplied server address. Try again - // using HostDB. + if (ResolveInfo::OS_Addr::TRY_CLIENT == s->dns_info.os_addr_style) { + // attempt was based on client supplied server address. Try again using HostDB. // Allow DNS attempt - s->dns_info.lookup_success = false; + s->dns_info.resolved_p = false; // See if we can get data from HostDB for this. - s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_TRY_HOSTDB; + s->dns_info.os_addr_style = ResolveInfo::OS_Addr::TRY_HOSTDB; // Force host resolution to have the same family as the client. // Because this is a transparent connection, we can't switch address // families - that is locked in by the client source address. ats_force_order_by_family(&s->current.server->dst_addr.sa, s->my_txn_conf().host_res_data.order); return CallOSDNSLookup(s); - } else if ((s->dns_info.srv_lookup_success || s->host_db_info.is_rr_elt()) && - (s->txn_conf->connect_attempts_rr_retries > 0) && - ((s->current.attempts + 1) % s->txn_conf->connect_attempts_rr_retries == 0)) { - delete_server_rr_entry(s, max_connect_retries); - return; } else { + if ((s->txn_conf->connect_attempts_rr_retries > 0) && + ((s->current.attempts + 1) % s->txn_conf->connect_attempts_rr_retries == 0)) { + s->dns_info.select_next_rr(); + } retry_server_connection_not_open(s, s->current.state, max_connect_retries); TxnDebug("http_trans", "[handle_response_from_server] Error. Retrying..."); s->next_action = how_to_open_connection(s); - - if (s->api_server_addr_set) { - // If the plugin set a server address, back up to the OS_DNS hook - // to let it try another one. Force OS_ADDR_USE_CLIENT so that - // in OSDNSLoopkup, we back up to how_to_open_connections which - // will tell HttpSM to connect the origin server. - - s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT; - TRANSACT_RETURN(SM_ACTION_API_OS_DNS, OSDNSLookup); - } - return; } } else { error_log_connection_failure(s, s->current.state); @@ -3860,36 +3789,6 @@ HttpTransact::handle_response_from_server(State *s) return; } -/////////////////////////////////////////////////////////////////////////////// -// Name : delete_server_rr_entry -// Description: -// -// Details : -// -// connection to server failed mark down the server round robin entry -// -// -// Possible Next States From Here: -// -/////////////////////////////////////////////////////////////////////////////// -void -HttpTransact::delete_server_rr_entry(State *s, int max_retries) -{ - char addrbuf[INET6_ADDRSTRLEN]; - - TxnDebug("http_trans", "[%d] failed to connect to %s", s->current.attempts, - ats_ip_ntop(&s->current.server->dst_addr.sa, addrbuf, sizeof(addrbuf))); - TxnDebug("http_trans", "[delete_server_rr_entry] marking rr entry " - "down and finding next one"); - ink_assert(s->current.server->had_connect_fail()); - ink_assert(s->current.request_to == ORIGIN_SERVER); - ink_assert(s->current.server == &s->server_info); - update_dns_info(&s->dns_info, &s->current); - s->current.attempts++; - TxnDebug("http_trans", "[delete_server_rr_entry] attempts now: %d, max: %d", s->current.attempts, max_retries); - TRANSACT_RETURN(SM_ACTION_ORIGIN_SERVER_RR_MARK_DOWN, ReDNSRoundRobin); -} - void HttpTransact::error_log_connection_failure(State *s, ServerState_t conn_state) { @@ -4049,7 +3948,7 @@ HttpTransact::handle_forward_server_connection_open(State *s) ink_release_assert(s->current.state == CONNECTION_ALIVE); HTTPVersion real_version = s->state_machine->get_server_version(s->hdr_info.server_response); - if (real_version != s->host_db_info.app.http_data.http_version) { + if (real_version != s->dns_info.http_version) { // Need to update the hostdb s->updated_server_version = real_version; TxnDebug("http_trans", "Update hostdb history of server HTTP version 0x%x", s->updated_server_version.get_flat_version()); @@ -5246,22 +5145,22 @@ HttpTransact::get_ka_info_from_host_db(State *s, ConnectionAttributes *server_in break; } - if (force_http11 == true || (http11_if_hostdb == true && host_db_info->app.http_data.http_version == HTTP_1_1)) { + if (force_http11 == true || (http11_if_hostdb == true && host_db_info->http_version == HTTP_1_1)) { server_info->http_version = HTTP_1_1; server_info->keep_alive = HTTP_KEEPALIVE; - } else if (host_db_info->app.http_data.http_version == HTTP_1_0) { + } else if (host_db_info->http_version == HTTP_1_0) { server_info->http_version = HTTP_1_0; server_info->keep_alive = HTTP_KEEPALIVE; - } else if (host_db_info->app.http_data.http_version == HTTP_0_9) { + } else if (host_db_info->http_version == HTTP_0_9) { server_info->http_version = HTTP_0_9; server_info->keep_alive = HTTP_NO_KEEPALIVE; } else { ////////////////////////////////////////////// // not set yet for this host. set defaults. // ////////////////////////////////////////////// - server_info->http_version = HTTP_1_0; - server_info->keep_alive = HTTP_KEEPALIVE; - host_db_info->app.http_data.http_version = HTTP_1_0; + server_info->http_version = HTTP_1_0; + server_info->keep_alive = HTTP_KEEPALIVE; + host_db_info->http_version = HTTP_1_0; } ///////////////////////////// @@ -5786,7 +5685,7 @@ HttpTransact::initialize_state_variables_from_request(State *s, HTTPHdr *obsolet // the expanded host for cache lookup, and // // the host ip for reverse proxy. // ///////////////////////////////////////////// - s->dns_info.looking_up = ORIGIN_SERVER; + s->dns_info.looking_up = ResolveInfo::ORIGIN_SERVER; s->dns_info.lookup_name = s->server_info.name; } @@ -6176,7 +6075,7 @@ HttpTransact::is_response_cacheable(State *s, HTTPHdr *request, HTTPHdr *respons // host addresses, do not allow cache. This may cause DNS cache poisoning // of other trafficserver clients. The flag is set in the // process_host_db_info method - if (!s->dns_info.lookup_validated && s->client_info.is_transparent) { + if (!s->dns_info.cta_validated_p && s->client_info.is_transparent) { TxnDebug("http_trans", "[is_response_cacheable] " "Lookup not validated. Possible DNS cache poison. Don't cache"); return false; @@ -6578,7 +6477,7 @@ HttpTransact::will_this_request_self_loop(State *s) //////////////////////////////////////// // check if we are about to self loop // //////////////////////////////////////// - if (s->dns_info.lookup_success) { + if (s->dns_info.active) { TxnDebug("http_transact", "max_proxy_cycles = %d", max_proxy_cycles); if (max_proxy_cycles == 0) { in_port_t dst_port = s->hdr_info.client_request.url_get()->port_get(); // going to this port. @@ -6586,13 +6485,13 @@ HttpTransact::will_this_request_self_loop(State *s) // It's a loop if connecting to the same port as it already connected to the proxy and // it's a proxy address or the same address it already connected to. TxnDebug("http_transact", "dst_port = %d local_port = %d", dst_port, local_port); - if (dst_port == local_port && (ats_ip_addr_eq(s->host_db_info.ip(), &Machine::instance()->ip.sa) || - ats_ip_addr_eq(s->host_db_info.ip(), s->client_info.dst_addr))) { + if (dst_port == local_port && ((s->dns_info.active->data.ip == &Machine::instance()->ip.sa) || + (s->dns_info.active->data.ip == s->client_info.dst_addr))) { switch (s->dns_info.looking_up) { - case ORIGIN_SERVER: + case ResolveInfo::ORIGIN_SERVER: TxnDebug("http_transact", "host ip and port same as local ip and port - bailing"); break; - case PARENT_PROXY: + case ResolveInfo::PARENT_PROXY: TxnDebug("http_transact", "parent proxy ip and port same as local ip and port - bailing"); break; default: @@ -6821,7 +6720,7 @@ HttpTransact::handle_request_keep_alive_headers(State *s, HTTPVersion ver, HTTPH case KA_CONNECTION: ink_assert(s->current.server->keep_alive != HTTP_NO_KEEPALIVE); if (ver == HTTP_1_0) { - if (s->current.request_to == PARENT_PROXY && parent_is_proxy(s)) { + if (s->current.request_to == ResolveInfo::PARENT_PROXY && parent_is_proxy(s)) { heads->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "keep-alive", 10); } else { heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, "keep-alive", 10); @@ -6835,7 +6734,7 @@ HttpTransact::handle_request_keep_alive_headers(State *s, HTTPVersion ver, HTTPH if (s->current.server->keep_alive != HTTP_NO_KEEPALIVE || (ver == HTTP_1_1)) { /* Had keep-alive */ s->current.server->keep_alive = HTTP_NO_KEEPALIVE; - if (s->current.request_to == PARENT_PROXY && parent_is_proxy(s)) { + if (s->current.request_to == ResolveInfo::PARENT_PROXY && parent_is_proxy(s)) { heads->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "close", 5); } else { heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, "close", 5); @@ -7753,7 +7652,7 @@ HttpTransact::build_request(State *s, HTTPHdr *base_request, HTTPHdr *outgoing_r if (outgoing_request->method_get_wksidx() == HTTP_WKSIDX_CONNECT) { // CONNECT method requires a target in the URL, so always force it from the Host header. outgoing_request->set_url_target_from_host_field(); - } else if (s->current.request_to == PARENT_PROXY && parent_is_proxy(s)) { + } else if (s->current.request_to == ResolveInfo::PARENT_PROXY && parent_is_proxy(s)) { // If we have a parent proxy set the URL target field. if (!outgoing_request->is_target_in_url()) { TxnDebug("http_trans", "[build_request] adding target to URL for parent proxy"); @@ -8763,7 +8662,7 @@ HttpTransact::update_size_and_time_stats(State *s, ink_hrtime total_time, ink_hr HTTP_SUM_DYN_STAT(http_user_agent_response_document_total_size_stat, user_agent_response_body_size); // proxy stats - if (s->current.request_to == HttpTransact::PARENT_PROXY) { + if (s->current.request_to == ResolveInfo::PARENT_PROXY) { HTTP_SUM_DYN_STAT(http_parent_proxy_request_total_bytes_stat, origin_server_request_header_size + origin_server_request_body_size); HTTP_SUM_DYN_STAT(http_parent_proxy_response_total_bytes_stat, diff --git a/proxy/http/HttpTransact.h b/proxy/http/HttpTransact.h index bb432c6d520..6da2385aac9 100644 --- a/proxy/http/HttpTransact.h +++ b/proxy/http/HttpTransact.h @@ -281,14 +281,6 @@ class HttpTransact HTTP_TRANSACT_MAGIC_SEPARATOR = 0x12345678 }; - enum LookingUp_t { - ORIGIN_SERVER, - UNDEFINED_LOOKUP, - PARENT_PROXY, - INCOMING_ROUTER, - HOST_NONE, - }; - enum ProxyMode_t { UNDEFINED_MODE, GENERIC_PROXY, @@ -573,57 +565,19 @@ class HttpTransact }; typedef struct _CurrentInfo { - ProxyMode_t mode = UNDEFINED_MODE; - LookingUp_t request_to = UNDEFINED_LOOKUP; - ConnectionAttributes *server = nullptr; - ink_time_t now = 0; - ServerState_t state = STATE_UNDEFINED; - unsigned attempts = 0; - unsigned simple_retry_attempts = 0; - unsigned unavailable_server_retry_attempts = 0; - ParentRetry_t retry_type = PARENT_RETRY_NONE; + ProxyMode_t mode = UNDEFINED_MODE; + ResolveInfo::UpstreamResolveStyle request_to = ResolveInfo::UNDEFINED_LOOKUP; + ConnectionAttributes *server = nullptr; + ink_time_t now = 0; + ServerState_t state = STATE_UNDEFINED; + unsigned attempts = 0; + unsigned simple_retry_attempts = 0; + unsigned unavailable_server_retry_attempts = 0; + ParentRetry_t retry_type = PARENT_RETRY_NONE; _CurrentInfo() {} } CurrentInfo; - typedef struct _DNSLookupInfo { - /** Origin server address source selection. - - If config says to use CTA (client target addr) state is - OS_ADDR_TRY_CLIENT, otherwise it remains the default. If the - connect fails then we switch to a USE. We go to USE_HOSTDB if - (1) the HostDB lookup is successful and (2) some address other - than the CTA is available to try. Otherwise we keep retrying - on the CTA (USE_CLIENT) up to the max retry value. In essence - we try to treat the CTA as if it were another RR value in the - HostDB record. - */ - enum class OS_Addr { - OS_ADDR_TRY_DEFAULT, ///< Initial state, use what config says. - OS_ADDR_TRY_HOSTDB, ///< Try HostDB data. - OS_ADDR_TRY_CLIENT, ///< Try client target addr. - OS_ADDR_USE_HOSTDB, ///< Force use of HostDB target address. - OS_ADDR_USE_CLIENT ///< Use client target addr, no fallback. - }; - - OS_Addr os_addr_style = OS_Addr::OS_ADDR_TRY_DEFAULT; - - bool lookup_success = false; - char *lookup_name = nullptr; - char srv_hostname[MAXDNAME] = {0}; - LookingUp_t looking_up = UNDEFINED_LOOKUP; - bool srv_lookup_success = false; - short srv_port = 0; - HostDBApplicationInfo srv_app; - - /*** Set to true by default. If use_client_target_address is set - * to 1, this value will be set to false if the client address is - * not in the DNS pool */ - bool lookup_validated = true; - - _DNSLookupInfo() {} - } DNSLookupInfo; - // Conversion handling for DNS host resolution type. static const MgmtConverter HOST_RES_CONV; @@ -671,7 +625,7 @@ class HttpTransact HttpConfigParams *http_config_param = nullptr; CacheLookupInfo cache_info; - DNSLookupInfo dns_info; + ResolveInfo dns_info; RedirectInfo redirect_info; OutboundConnTrack::TxnState outbound_conn_track_state; HTTPVersion updated_server_version = HTTP_INVALID; @@ -723,8 +677,6 @@ class HttpTransact int orig_scheme = scheme; // pre-mapped scheme int method = 0; int cause_of_death_errno = -UNKNOWN_INTERNAL_ERROR; // in - Ptr hostdb_entry; // Pointer to the entry we are referencing in hostdb-- to keep our ref - HostDBInfo host_db_info; // in ink_time_t client_request_time = UNDEFINED_TIME; // internal ink_time_t request_sent_time = UNDEFINED_TIME; // internal @@ -773,7 +725,6 @@ class HttpTransact bool api_server_request_body_set = false; bool api_req_cacheable = false; bool api_resp_cacheable = false; - bool api_server_addr_set = false; UpdateCachedObject_t api_update_cached_object = UPDATE_CACHED_OBJECT_NONE; StateMachineAction_t saved_update_next_action = SM_ACTION_UNDEFINED; CacheAction_t saved_update_cache_action = CACHE_DO_UNDEFINED; @@ -821,6 +772,7 @@ class HttpTransact init() { parent_params = ParentConfig::acquire(); + new (&dns_info) decltype(dns_info); // reset to default state. } // Constructor @@ -847,7 +799,8 @@ class HttpTransact via_string[VIA_DETAIL_SERVER_DESCRIPTOR] = VIA_DETAIL_SERVER_DESCRIPTOR_STRING; via_string[MAX_VIA_INDICES] = '\0'; - memset((void *)&host_db_info, 0, sizeof(host_db_info)); + // memset(user_args, 0, sizeof(user_args)); + // memset((void *)&host_db_info, 0, sizeof(host_db_info)); } void @@ -877,7 +830,8 @@ class HttpTransact url_map.clear(); arena.reset(); unmapped_url.clear(); - hostdb_entry.clear(); + // hostdb_entry.clear(); + dns_info.~ResolveInfo(); outbound_conn_track_state.clear(); delete[] ranges; @@ -921,6 +875,7 @@ class HttpTransact if (e != EIO) { this->cause_of_death_errno = e; } + Debug("http", "Setting upstream connection failure %d to %d", e, this->current.server->connect_result); } private: @@ -952,7 +907,6 @@ class HttpTransact static void CallOSDNSLookup(State *s); static void OSDNSLookup(State *s); - static void ReDNSRoundRobin(State *s); static void PPDNSLookup(State *s); static void PPDNSLookupAPICall(State *s); static void OriginServerRawOpen(State *s); diff --git a/proxy/http/remap/unit-tests/nexthop_test_stubs.cc b/proxy/http/remap/unit-tests/nexthop_test_stubs.cc index 48b3c13561e..73ea19de7fb 100644 --- a/proxy/http/remap/unit-tests/nexthop_test_stubs.cc +++ b/proxy/http/remap/unit-tests/nexthop_test_stubs.cc @@ -84,7 +84,6 @@ br_destroy(HttpSM &sm) } delete h->hdr; delete h->api_info; - ats_free(h->hostname_str); } void @@ -100,10 +99,7 @@ build_request(int64_t sm_id, HttpSM *sm, sockaddr_in *ip, const char *os_hostnam } sm->t_state.request_data.hdr = new HTTPHdr(); sm->t_state.request_data.hdr->create(HTTP_TYPE_REQUEST, myHeap); - if (sm->t_state.request_data.hostname_str != nullptr) { - ats_free(sm->t_state.request_data.hostname_str); - } - sm->t_state.request_data.hostname_str = ats_strdup(os_hostname); + sm->t_state.request_data.hostname_str = sm->t_state.arena.str_store(os_hostname, strlen(os_hostname)); sm->t_state.request_data.xact_start = time(nullptr); ink_zero(sm->t_state.request_data.src_ip); ink_zero(sm->t_state.request_data.dest_ip); diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc index 47cb4df72fc..a163be300fb 100644 --- a/src/traffic_server/InkAPI.cc +++ b/src/traffic_server/InkAPI.cc @@ -5849,12 +5849,11 @@ TSHttpTxnServerAddrSet(TSHttpTxn txnp, struct sockaddr const *addr) sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); HttpSM *sm = reinterpret_cast(txnp); - if (ats_ip_copy(&sm->t_state.server_info.dst_addr.sa, addr)) { - sm->t_state.api_server_addr_set = true; + if (sm->t_state.dns_info.set_upstream_address(addr)) { + sm->t_state.dns_info.os_addr_style = ResolveInfo::OS_Addr::USE_API; return TS_SUCCESS; - } else { - return TS_ERROR; } + return TS_ERROR; } void @@ -7406,12 +7405,18 @@ TSHostLookup(TSCont contp, const char *hostname, size_t namelen) return (TSAction)hostDBProcessor.getbyname_re(i, hostname, namelen); } -sockaddr const * -TSHostLookupResultAddrGet(TSHostLookupResult lookup_result) +TSReturnCode +TSHostLookupResultAddrGet(TSHostLookupResult lookup_result, sockaddr *dst) { sdk_assert(sdk_sanity_check_hostlookup_structure(lookup_result) == TS_SUCCESS); - HostDBInfo *di = reinterpret_cast(lookup_result); - return di->ip(); + HostDBRecord::Handle record{reinterpret_cast(lookup_result)}; + if (record) { + auto info = record->rr_info(); + sdk_assert(info.size() > 0); + info[0].data.ip.toSockAddr(dst); + return TS_SUCCESS; + } + return TS_ERROR; } /* @@ -8452,6 +8457,9 @@ _memberp_to_generic(MgmtFloat *ptr, MgmtConverter const *&conv) -> typename std: static void * _conf_to_memberp(TSOverridableConfigKey conf, OverridableHttpConfigParams *overridableHttpConfig, MgmtConverter const *&conv) { + // External converters. + extern MgmtConverter const &HostDBDownServerCacheTimeConv; + void *ret = nullptr; conv = nullptr; @@ -8607,7 +8615,8 @@ _conf_to_memberp(TSOverridableConfigKey conf, OverridableHttpConfigParams *overr ret = _memberp_to_generic(&overridableHttpConfig->connect_attempts_timeout, conv); break; case TS_CONFIG_HTTP_DOWN_SERVER_CACHE_TIME: - ret = _memberp_to_generic(&overridableHttpConfig->down_server_timeout, conv); + conv = &HostDBDownServerCacheTimeConv; + ret = &overridableHttpConfig->down_server_timeout; break; case TS_CONFIG_HTTP_DOWN_SERVER_ABORT_THRESHOLD: ret = _memberp_to_generic(&overridableHttpConfig->client_abort_threshold, conv); diff --git a/src/traffic_server/InkAPITest.cc b/src/traffic_server/InkAPITest.cc index af0374eca9f..f5d11829851 100644 --- a/src/traffic_server/InkAPITest.cc +++ b/src/traffic_server/InkAPITest.cc @@ -8512,14 +8512,14 @@ EXCLUSIVE_REGRESSION_TEST(SDK_API_TSHttpConnectIntercept)(RegressionTest *test, /* ip and log do not matter as it is used for logging only */ sockaddr_in addr; ats_ip4_set(&addr, 1, 1); - data->vc = TSHttpConnect(ats_ip_sa_cast(&addr)); + data->vc = TSHttpConnectWithPluginId(ats_ip_sa_cast(&addr), "TSHttpConnectIntercept", 1); if (TSVConnClosedGet(data->vc)) { SDK_RPRINT(data->test, "TSHttpConnect", "TestCase 1", TC_FAIL, "Connect reported as closed immediately after open"); } synclient_txn_send_request_to_vc(data->browser, data->request, data->vc); /* Wait until transaction is done */ - TSContScheduleOnPool(cont_test, 25, TS_THREAD_POOL_NET); + TSContScheduleOnPool(cont_test, 100, TS_THREAD_POOL_NET); return; } @@ -8553,12 +8553,12 @@ EXCLUSIVE_REGRESSION_TEST(SDK_API_TSHttpConnectServerIntercept)(RegressionTest * /* ip and log do not matter as it is used for logging only */ sockaddr_in addr; ats_ip4_set(&addr, 2, 2); - data->vc = TSHttpConnect(ats_ip_sa_cast(&addr)); + data->vc = TSHttpConnectWithPluginId(ats_ip_sa_cast(&addr), "TSHttpConnectServerIntercept", 1); synclient_txn_send_request_to_vc(data->browser, data->request, data->vc); /* Wait until transaction is done */ - TSContScheduleOnPool(cont_test, 25, TS_THREAD_POOL_NET); + TSContScheduleOnPool(cont_test, 100, TS_THREAD_POOL_NET); return; } From 1c207c549a7e36e44dc3a33c3114484ab70c9126 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Wed, 26 May 2021 13:35:12 -0500 Subject: [PATCH 13/33] Minor fixes, clean up some printf formatting. --- include/ts/ts.h | 2 +- include/tscore/Diags.h | 9 ++++ iocore/hostdb/HostDB.cc | 77 ++++++++++++++++--------------- iocore/hostdb/I_HostDBProcessor.h | 6 +-- iocore/hostdb/P_HostDBProcessor.h | 3 +- proxy/http/HttpSM.cc | 10 ++-- 6 files changed, 60 insertions(+), 47 deletions(-) diff --git a/include/ts/ts.h b/include/ts/ts.h index 64246a0839f..03ff78c9914 100644 --- a/include/ts/ts.h +++ b/include/ts/ts.h @@ -1921,7 +1921,7 @@ tsapi TSAction TSHostLookup(TSCont contp, const char *hostname, size_t namelen); /** Retrieve an address from the host lookup. * * @param lookup_result Result handle passed to event callback. - * @param dst Destiation for copying the addres. + * @param dst Destination for copying the address. * @return @c TS_SUCCESS if the address was available and copied, @c TS_ERROR otherwise. */ tsapi TSReturnCode TSHostLookupResultAddrGet(TSHostLookupResult lookup_result, struct sockaddr *dst); diff --git a/include/tscore/Diags.h b/include/tscore/Diags.h index be3e6bf4b2f..70ba2bfd740 100644 --- a/include/tscore/Diags.h +++ b/include/tscore/Diags.h @@ -159,6 +159,15 @@ extern inkcoreapi Diags *diags; } \ } while (0) +#define Debug_bw(tag, fmt, ...) \ + do { \ + if (unlikely(diags->on())) { \ + static const SourceLocation loc = MakeSourceLocation(); \ + static LogMessage log_message; \ + log_message.debug(tag, loc, "%s", ts::bwprint(ts::bw_dbg, fmt, __VA_ARGS__).c_str()); \ + } \ + } while (0) + /** Same as Debug above, but this allows a positive override of the tag * mechanism by a flag boolean. * diff --git a/iocore/hostdb/HostDB.cc b/iocore/hostdb/HostDB.cc index 6f1843ef418..220cb948ff1 100644 --- a/iocore/hostdb/HostDB.cc +++ b/iocore/hostdb/HostDB.cc @@ -999,13 +999,13 @@ HostDBContinuation::lookup_done(TextView query_name, ts_seconds answer_ttl, SRVH record->ip_timeout_interval = std::clamp(answer_ttl, ts_seconds(1), ts_seconds(HOST_DB_MAX_TTL)); if (is_byname()) { - Debug("hostdb", "done %.*s TTL %ld", int(hash.host_name.size()), hash.host_name.data(), answer_ttl.count()); + Debug_bw("hostdb", "done {} TTL {}", hash.host_name, answer_ttl); } else if (is_srv()) { ink_assert(srv && srv->hosts.size() && srv->hosts.size() <= hostdb_round_robin_max_count); record->record_type = HostDBType::SRV; } else { - Debug("hostdb", "done '%.*s' TTL %ld", int(query_name.size()), query_name.data(), answer_ttl.count()); + Debug_bw("hostdb", "done {} TTL {}", hash.host_name, answer_ttl); record->record_type = HostDBType::HOST; } } @@ -1175,8 +1175,8 @@ HostDBContinuation::dnsEvent(int event, HostEnt *e) } } } - - Debug("dns_srv", "inserted SRV RR record [%s] into HostDB with TTL: %ld seconds", t->host, ttl.count()); + // Archetypical example - "%zd" doesn't work on FreeBSD, "%ld" doesn't work on Ubuntu, "%lld" doesn't work on Fedora. + Debug_bw("dns_srv", "inserted SRV RR record [{}] into HostDB with TTL: {} seconds", t->host, ttl); } } else { // Otherwise this is a regular dns response unsigned idx = 0; @@ -1467,33 +1467,33 @@ HostDBContinuation::do_dns() } } } + } - if (hostdb_lookup_timeout) { - timeout = mutex->thread_holding->schedule_in(this, HRTIME_SECONDS(hostdb_lookup_timeout)); - } else { - timeout = nullptr; - } - if (set_check_pending_dns()) { - DNSProcessor::Options opt; - opt.timeout = dns_lookup_timeout; - opt.host_res_style = host_res_style_for(hash.db_mark); - SET_HANDLER((HostDBContHandler)&HostDBContinuation::dnsEvent); - if (is_byname()) { - if (hash.dns_server) { - opt.handler = hash.dns_server->x_dnsH; - } - pending_action = dnsProcessor.gethostbyname(this, hash.host_name, opt); - } else if (is_srv()) { - Debug("dns_srv", "SRV lookup of %.*s", int(hash.host_name.size()), hash.host_name.data()); - pending_action = dnsProcessor.getSRVbyname(this, hash.host_name, opt); - } else { - ip_text_buffer ipb; - Debug("hostdb", "DNS IP %s", hash.ip.toString(ipb, sizeof ipb)); - pending_action = dnsProcessor.gethostbyaddr(this, &hash.ip, opt); + if (hostdb_lookup_timeout) { + timeout = mutex->thread_holding->schedule_in(this, HRTIME_SECONDS(hostdb_lookup_timeout)); + } else { + timeout = nullptr; + } + if (set_check_pending_dns()) { + DNSProcessor::Options opt; + opt.timeout = dns_lookup_timeout; + opt.host_res_style = host_res_style_for(hash.db_mark); + SET_HANDLER((HostDBContHandler)&HostDBContinuation::dnsEvent); + if (is_byname()) { + if (hash.dns_server) { + opt.handler = hash.dns_server->x_dnsH; } + pending_action = dnsProcessor.gethostbyname(this, hash.host_name, opt); + } else if (is_srv()) { + Debug("dns_srv", "SRV lookup of %.*s", int(hash.host_name.size()), hash.host_name.data()); + pending_action = dnsProcessor.getSRVbyname(this, hash.host_name, opt); } else { - SET_HANDLER((HostDBContHandler)&HostDBContinuation::dnsPendingEvent); + ip_text_buffer ipb; + Debug("hostdb", "DNS IP %s", hash.ip.toString(ipb, sizeof ipb)); + pending_action = dnsProcessor.gethostbyaddr(this, &hash.ip, opt); } + } else { + SET_HANDLER((HostDBContHandler)&HostDBContinuation::dnsPendingEvent); } } @@ -2011,6 +2011,12 @@ std::atomic HostDBFileUpdateActive{false}; */ using HostAddrMap = std::unordered_map, std::vector>>; +namespace +{ +constexpr unsigned IPV4_IDX = 0; +constexpr unsigned IPV6_IDX = 1; +} // namespace + static void ParseHostLine(TextView line, HostAddrMap &map) { @@ -2026,9 +2032,9 @@ ParseHostLine(TextView line, HostAddrMap &map) while (!line.ltrim_if(&isspace).empty()) { TextView name = line.take_prefix_if(&isspace); if (addr.isIp6()) { - std::get<1>(map[name]).push_back(addr); + std::get(map[name]).push_back(addr); } else if (addr.isIp4()) { - std::get<0>(map[name]).push_back(addr); + std::get(map[name]).push_back(addr); } } } @@ -2037,7 +2043,6 @@ void ParseHostFile(ts::file::path const &path, ts_seconds hostdb_hostfile_check_interval_parse) { std::shared_ptr map; - std::string dbg; // Test and set for update in progress. bool flag = false; @@ -2045,7 +2050,7 @@ ParseHostFile(ts::file::path const &path, ts_seconds hostdb_hostfile_check_inter Debug("hostdb", "Skipped load of host file because update already in progress"); return; } - Debug("hostdb", "%s", ts::bwprint(dbg, R"(Loading host file "{}")", path).c_str()); + Debug_bw("hostdb", R"(Loading host file "{}")", path); if (!path.empty()) { std::error_code ec; @@ -2061,7 +2066,7 @@ ParseHostFile(ts::file::path const &path, ts_seconds hostdb_hostfile_check_inter ParseHostLine(line, addr_map); } // @a map should be loaded with all of the data, create the records. - map.reset(new HostFileMap); + map = std::make_shared(); // Common loading function for creating a record from the address vector. auto loader = [](TextView key, std::vector const &v) -> HostDBRecord::Handle { HostDBRecord::Handle record{HostDBRecord::alloc(key, v.size())}; @@ -2073,7 +2078,7 @@ ParseHostFile(ts::file::path const &path, ts_seconds hostdb_hostfile_check_inter } return record; }; - // Walk the host file map and create the corresponding records. + // Walk the temporary map and create the corresponding records for the persistent map. for (auto const &[key, value] : addr_map) { // Bit of subtlety to be able to search records with a view and not a string - the key // must point at stable memory for the name, which is available in the record itself. @@ -2081,14 +2086,14 @@ ParseHostFile(ts::file::path const &path, ts_seconds hostdb_hostfile_check_inter // It doesn't matter if it's the IPv4 or IPv6 record that's used, both are stable and equal // to each other. // IPv4 - if (auto const &v = std::get<0>(value); v.size() > 0) { + if (auto const &v = std::get(value); v.size() > 0) { auto r = loader(key, v); (*map)[r->name_view()].record_4 = r; } // IPv6 - if (auto const &v = std::get<1>(value); v.size() > 0) { + if (auto const &v = std::get(value); v.size() > 0) { auto r = loader(key, v); - (*map)[r->name_view()].record_4 = r; + (*map)[r->name_view()].record_6 = r; } } diff --git a/iocore/hostdb/I_HostDBProcessor.h b/iocore/hostdb/I_HostDBProcessor.h index 2877969b470..4fb03688865 100644 --- a/iocore/hostdb/I_HostDBProcessor.h +++ b/iocore/hostdb/I_HostDBProcessor.h @@ -49,7 +49,7 @@ // // Data // -class HostDBContinuation; +struct HostDBContinuation; struct ResolveInfo; // @@ -290,7 +290,7 @@ HostDBInfo::invalidate() */ class HostDBRecord : public RefCountObj { - friend class HostDBContinuation; + friend struct HostDBContinuation; friend struct ShowHostDB; using self_type = HostDBRecord; @@ -819,7 +819,7 @@ inline bool ResolveInfo::mark_active_server_alive() { if (zombie_p && active) { - active->mark_up(); + return active->mark_up(); } zombie_p = false; return false; diff --git a/iocore/hostdb/P_HostDBProcessor.h b/iocore/hostdb/P_HostDBProcessor.h index 8e9b76dc7be..637f3a2235e 100644 --- a/iocore/hostdb/P_HostDBProcessor.h +++ b/iocore/hostdb/P_HostDBProcessor.h @@ -222,8 +222,7 @@ struct HostDBHash { // // Handles a HostDB lookup request // -struct HostDBContinuation; -typedef int (HostDBContinuation::*HostDBContHandler)(int, void *); +using HostDBContHandler = int (HostDBContinuation::*)(int, void *); struct HostDBContinuation : public Continuation { Action action; diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc index 32494a5cd2f..513e185c525 100644 --- a/proxy/http/HttpSM.cc +++ b/proxy/http/HttpSM.cc @@ -1867,7 +1867,7 @@ HttpSM::state_http_server_open(int event, void *data) NetVConnection *netvc = static_cast(data); UnixNetVConnection *vc = static_cast(data); PoolableSession *new_session = this->create_server_session(netvc); - if (t_state.current.request_to == HttpTransact::PARENT_PROXY) { + if (t_state.current.request_to == ResolveInfo::PARENT_PROXY) { new_session->to_parent_proxy = true; HTTP_INCREMENT_DYN_STAT(http_current_parent_proxy_connections_stat); HTTP_INCREMENT_DYN_STAT(http_total_parent_proxy_connections_stat); @@ -5345,13 +5345,13 @@ HttpSM::mark_host_failure(ResolveInfo *info, ts_time time_down) if (info->active->last_failure.load() == TS_TIME_ZERO) { char *url_str = t_state.hdr_info.client_request.url_string_get(&t_state.arena, nullptr); int host_len; - const char *host_name_ptr = t_state.unmapped_url.host_get(&host_len); - std::string_view host_name{host_name_ptr, size_t(host_len)}; - Log::error("%s", lbw() + const char *host_name_ptr = t_state.unmapped_url.host_get(&host_len); + std::string_view host_name{host_name_ptr, size_t(host_len)}; + Log::error("%s", lbw() .clip(1) .print("CONNECT : {::s} connecting to {} for host='{}' url='{}' marking down", ts::bwf::Errno(t_state.current.server->connect_result), t_state.current.server->dst_addr, - host_name,ts::bwf::FirstOf(url_str, "")) + host_name, ts::bwf::FirstOf(url_str, "")) .extend(1) .write('\0') .data()); From a84ceb1dd8a64662ac7b45867e909586c28b4c27 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Thu, 3 Jun 2021 12:30:51 -0500 Subject: [PATCH 14/33] Tweak TLS test to not mark server down for TLS errors. --- tests/gold_tests/tls/tls_verify_override_base.test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/gold_tests/tls/tls_verify_override_base.test.py b/tests/gold_tests/tls/tls_verify_override_base.test.py index 1274185ea92..2217a2760e5 100644 --- a/tests/gold_tests/tls/tls_verify_override_base.test.py +++ b/tests/gold_tests/tls/tls_verify_override_base.test.py @@ -114,7 +114,8 @@ 'proxy.config.exec_thread.autoconfig.scale': 1.0, 'proxy.config.dns.nameservers': '127.0.0.1:{0}'.format(dns.Variables.Port), 'proxy.config.dns.resolv_conf': 'NULL', - 'proxy.config.ssl.client.verify.server.policy': 'PERMISSIVE' + 'proxy.config.ssl.client.verify.server.policy': 'PERMISSIVE', + 'proxy.config.http.connect.dead.policy' : 1, # Don't count TLS failures for dead upstream. }) dns.addRecords(records={"foo.com.": ["127.0.0.1"]}) From 6492503b7f6d311f3789c15d3b743aeb951e7ded Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Thu, 3 Jun 2021 13:21:59 -0500 Subject: [PATCH 15/33] FreeBSD integer format fix. --- iocore/hostdb/HostDB.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iocore/hostdb/HostDB.cc b/iocore/hostdb/HostDB.cc index 220cb948ff1..47c7bec2ec8 100644 --- a/iocore/hostdb/HostDB.cc +++ b/iocore/hostdb/HostDB.cc @@ -2267,8 +2267,8 @@ HostDBRecord::serve_stale_but_revalidate() const // hostdb_serve_stale_but_revalidate == number of seconds // ip_interval() is the number of seconds between now() and when the entry was inserted if ((ip_timeout_interval + ts_seconds(hostdb_serve_stale_but_revalidate)) > ip_interval()) { - Debug("hostdb", "serving stale entry %ld | %d | %ld as requested by config", ip_timeout_interval.count(), - hostdb_serve_stale_but_revalidate, ip_interval().count()); + Debug_bw("hostdb", "serving stale entry {} | {} | {} as requested by config", ip_timeout_interval, + hostdb_serve_stale_but_revalidate, ip_interval()); return true; } From 9d3a5183d0c68fe07a9548bcf7863134d197c8f7 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Thu, 3 Jun 2021 13:26:52 -0500 Subject: [PATCH 16/33] Fix autopep8 issue. --- tests/gold_tests/tls/tls_verify_override_base.test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gold_tests/tls/tls_verify_override_base.test.py b/tests/gold_tests/tls/tls_verify_override_base.test.py index 2217a2760e5..4b0b2d5680d 100644 --- a/tests/gold_tests/tls/tls_verify_override_base.test.py +++ b/tests/gold_tests/tls/tls_verify_override_base.test.py @@ -115,7 +115,7 @@ 'proxy.config.dns.nameservers': '127.0.0.1:{0}'.format(dns.Variables.Port), 'proxy.config.dns.resolv_conf': 'NULL', 'proxy.config.ssl.client.verify.server.policy': 'PERMISSIVE', - 'proxy.config.http.connect.dead.policy' : 1, # Don't count TLS failures for dead upstream. + 'proxy.config.http.connect.dead.policy': 1, # Don't count TLS failures for dead upstream. }) dns.addRecords(records={"foo.com.": ["127.0.0.1"]}) From f0b8fab9a6271789064b0fc7c56ff490ebfbd22a Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Wed, 9 Jun 2021 18:28:23 -0500 Subject: [PATCH 17/33] Fix strategies test, lack of update for down server configuration. --- iocore/hostdb/I_HostDBProcessor.h | 3 +-- proxy/http/HttpSM.cc | 2 +- proxy/http/HttpTransact.cc | 2 +- .../gold_tests/next_hop/strategies_ch2/strategies_ch2.test.py | 2 ++ 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/iocore/hostdb/I_HostDBProcessor.h b/iocore/hostdb/I_HostDBProcessor.h index 4fb03688865..1769a6f0f9f 100644 --- a/iocore/hostdb/I_HostDBProcessor.h +++ b/iocore/hostdb/I_HostDBProcessor.h @@ -559,8 +559,7 @@ struct ResolveInfo { /// This is the "resolved" address if @a resolved_p is @c true. IpEndpoint addr; - int attempts = 0; ///< Number of connection attempts. - ts_seconds fail_window{300}; ///< Down server blackout time (txn overridable) + int attempts = 0; ///< Number of connection attempts. char const *lookup_name = nullptr; char srv_hostname[MAXDNAME] = {0}; diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc index 513e185c525..2a9419d3478 100644 --- a/proxy/http/HttpSM.cc +++ b/proxy/http/HttpSM.cc @@ -2272,7 +2272,7 @@ HttpSM::process_hostdb_info(HostDBRecord *record) t_state.dns_info.inbound_remote_addr = &t_state.client_info.src_addr.sa; if (!use_client_addr) { t_state.dns_info.set_active( - record->select_best_http(ts_clock::now(), t_state.dns_info.fail_window, t_state.dns_info.inbound_remote_addr)); + record->select_best_http(ts_clock::now(), t_state.txn_conf->down_server_timeout, t_state.dns_info.inbound_remote_addr)); } else { // if use_client_target_addr is set, make sure the client addr is in the results pool t_state.dns_info.cta_validated_p = true; diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc index 63784cc63ad..960e649fdb7 100644 --- a/proxy/http/HttpTransact.cc +++ b/proxy/http/HttpTransact.cc @@ -1815,7 +1815,7 @@ HttpTransact::PPDNSLookup(State *s) } } else { // lookup succeeded, open connection to p.p. - // ats_ip_copy(&s->parent_info.dst_addr, s->host_db_info.ip()); + ats_ip_copy(&s->parent_info.dst_addr, s->dns_info.addr); s->parent_info.dst_addr.port() = htons(s->parent_result.port); get_ka_info_from_host_db(s, &s->parent_info, &s->client_info, s->dns_info.active); diff --git a/tests/gold_tests/next_hop/strategies_ch2/strategies_ch2.test.py b/tests/gold_tests/next_hop/strategies_ch2/strategies_ch2.test.py index 3dcf1d53996..f9cc72721e2 100644 --- a/tests/gold_tests/next_hop/strategies_ch2/strategies_ch2.test.py +++ b/tests/gold_tests/next_hop/strategies_ch2/strategies_ch2.test.py @@ -74,6 +74,7 @@ 'proxy.config.http.uncacheable_requests_bypass_parent': 0, 'proxy.config.http.no_dns_just_forward_to_parent': 1, 'proxy.config.http.parent_proxy.mark_down_hostdb': 1, + 'proxy.config.http.down_server.cache_time': 1, 'proxy.config.http.parent_proxy.self_detect': 0, }) @@ -90,6 +91,7 @@ # The health check URL does not seem to be used currently. # s.AddLine(f" health_check_url: http://next_hop{i}:{ts_nh[i].Variables.port}") s.AddLine(f" weight: 1.0") + s.AddLines([ "strategies:", " - strategy: the-strategy", From 7221fd19819be891d2a2c063a355cebefa68e095 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Thu, 10 Jun 2021 16:10:19 -0500 Subject: [PATCH 18/33] Remove zombie tracking - not really needed. --- iocore/hostdb/HostDB.cc | 3 --- iocore/hostdb/I_HostDBProcessor.h | 9 +-------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/iocore/hostdb/HostDB.cc b/iocore/hostdb/HostDB.cc index 47c7bec2ec8..90c3a995336 100644 --- a/iocore/hostdb/HostDB.cc +++ b/iocore/hostdb/HostDB.cc @@ -2380,9 +2380,6 @@ ResolveInfo::set_active(HostDBInfo *info) if (info) { addr.assign(active->data.ip); resolved_p = true; - if (info->last_fail_time() != TS_TIME_ZERO) { - zombie_p = true; - } return true; } resolved_p = false; diff --git a/iocore/hostdb/I_HostDBProcessor.h b/iocore/hostdb/I_HostDBProcessor.h index 1769a6f0f9f..bc3216fc768 100644 --- a/iocore/hostdb/I_HostDBProcessor.h +++ b/iocore/hostdb/I_HostDBProcessor.h @@ -583,9 +583,6 @@ struct ResolveInfo { * not in the DNS pool */ bool cta_validated_p = true; - /// If the target was selected while failed. - bool zombie_p = false; - bool set_active(HostDBInfo *info); bool set_active(sockaddr const *s); @@ -817,11 +814,7 @@ ResolveInfo::set_active(sockaddr const *s) inline bool ResolveInfo::mark_active_server_alive() { - if (zombie_p && active) { - return active->mark_up(); - } - zombie_p = false; - return false; + return active->mark_up(); } inline bool From eef9e5fb9e6747e4ff14c80fa5c7403c9337b34e Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Thu, 10 Jun 2021 23:47:42 -0500 Subject: [PATCH 19/33] autopep8 fix. --- tests/gold_tests/tls/tls_verify_override_base.test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gold_tests/tls/tls_verify_override_base.test.py b/tests/gold_tests/tls/tls_verify_override_base.test.py index 4b0b2d5680d..00027f230b9 100644 --- a/tests/gold_tests/tls/tls_verify_override_base.test.py +++ b/tests/gold_tests/tls/tls_verify_override_base.test.py @@ -115,7 +115,7 @@ 'proxy.config.dns.nameservers': '127.0.0.1:{0}'.format(dns.Variables.Port), 'proxy.config.dns.resolv_conf': 'NULL', 'proxy.config.ssl.client.verify.server.policy': 'PERMISSIVE', - 'proxy.config.http.connect.dead.policy': 1, # Don't count TLS failures for dead upstream. + 'proxy.config.http.connect.dead.policy': 1, # Don't count TLS failures for dead upstream. }) dns.addRecords(records={"foo.com.": ["127.0.0.1"]}) From d8b6194f3f69818f5abdb86ebb5d76f36f5bb182 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Wed, 16 Jun 2021 16:03:59 -0500 Subject: [PATCH 20/33] Documentation updates. --- .../core-architecture/HostDB-Data-Layout.svg | 3 + .../core-architecture/hostdb.en.rst | 111 +++++++++++++++++- .../core-architecture/index.en.rst | 1 + 3 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 doc/developer-guide/core-architecture/HostDB-Data-Layout.svg diff --git a/doc/developer-guide/core-architecture/HostDB-Data-Layout.svg b/doc/developer-guide/core-architecture/HostDB-Data-Layout.svg new file mode 100644 index 00000000000..9c02674a826 --- /dev/null +++ b/doc/developer-guide/core-architecture/HostDB-Data-Layout.svg @@ -0,0 +1,3 @@ + + +HostDBRecordHostDBType : record_typeunsigned: rr_offset

VLA<HostDBInfo>


HostDBInfo
.

.

.


VLA<HostDBInfo>...

SRV Names


Name
.

.

.

SRV Names...
HostDB Hash TableKeyHostDBRecordRecord 1Record 2Record 3

Optional
Optional

RR Data
RR Data

SRV only
SRV only
rr_offset indicates start of HostDBInfo VLA.
rr_offset indicates start o...
Each SRV record has an
offset to its name
Each SRV record has an...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/doc/developer-guide/core-architecture/hostdb.en.rst b/doc/developer-guide/core-architecture/hostdb.en.rst index fa3097be420..56ee7dbfaa5 100644 --- a/doc/developer-guide/core-architecture/hostdb.en.rst +++ b/doc/developer-guide/core-architecture/hostdb.en.rst @@ -26,15 +26,114 @@ HostDB ****** HostDB is a cache of DNS results. It is used to increase performance by aggregating address -resolution across transactions. +resolution across transactions. HostDB also stores state information for specific IP addresses. + +Operation +========= + +The primary operation for HostDB is to resolve a fully qualified domain name ("FQDN"). As noted each +FQDN is associated with a single record. Each record has an array of items. When a resolution +request is made the database is checked to see if the record is already present. If so, it is +served. Otherwise a DNS request is made. When the nameserver replies a record is created, added +to the database, and then returned to the requestor. + +Each info tracks several status values for its corresponding upstream. These are + +* HTTP version +* Last failure time + +The HTTP version is tracked from responses and provides a mechanism to make intelligent guesses +about the protocol to use to the upstream. + +The last failure time tracks when the last connection failure to the info occurred and doubles as +a flag, where a value of ``TS_TIME_ZERO`` indicates a live target and any other value indicates a +dead info. + +If an info is marked dead (has a non-zero last failure time) there is a "fail window" during which +no connections are permitted. After this time the info is considered to be a "zombie". If all infos +for a record are dead then a specific error message is generated (body factory tag +"connect#all_dead"). Otherwise if the selected info is a zombie, a request is permitted but the +zombie is immediately marked dead again, preventing any additional requests until either the fail +window has passed or the single connection succeeds. A successful connection clears the last file +time and the info becomes alive. Runtime Structure ================= -DNS results are stored in a global hash table as instances of ``HostDBRecord``. Each instance -stores the results of a single query. These records are not updated with new DNS results. Instead -a new record instance is created and replaces the previous instance in the table. Some specific -dynamic data is migrated from the old record to the new one, such as the failure status of the -upstreams in the record. +DNS results are stored in a global hash table as instances of ``HostDBRecord``. Each record stores +the results of a single query. These records are not updated with new DNS results - instead a new +record instance is created and replaces the previous instance in the table. The records are +reference counted so such a replacement doesn't invalidate the old record if the latter is still +being accessed. Some specific dynamic data is migrated from the old record to the new one, such as +the failure status of the upstreams in the record. + +In each record is a variable length array of items, instances of ``HostDBInfo``, one for each +IP address in the record. This is called the "round robin" data for historical reasons. For SRV +records there is an additional storage area in the record that is used to store the SRV names. + +.. figure:: HostDB-Data-Layout.svg + +The round robin data is accessed by using an offset and count in the base record. For SRV records +each record has an offset, relative to that ``HostDBInfo`` instance, for its own name in the name +storage area. + +State information for the outbound connection has been moved to a refurbished ``DNSInfo`` class +named ``ResolveInfo``. As much as possible relevant state information has been moved from the +``HttpSM`` to this structure. This is intended for future work where the state machine deals only +with upstream transactions and not sessions. + +``ResolveInfo`` may contain a reference to a HostDB record, which preserves the record even if it is +replaced due to DNS queries in other transactions. The record is not required as the resolution +information can be supplied directly without DNS or HostDB, e.g. a plugin sets the upstream address +explicitly. + +Issues +====== + +Currently if an upstream is marked down connections are still permitted, the only change is the +number of retries. This has caused operational problems where dead systems are flooded with requests +which, despite the timeouts, accumulate in ATS until ATS runs out of memory (there were instances of +over 800K pending transactions). This also made it hard to bring the upstreams back online. With +these changes requests to dead upstreams are strongly rate limited and other transactions are +immediately terminated with a 502 response, protecting both the upstream and ATS. + +Future +====== + +There is still some work to be done in future PRs. + +* The fail window and the zombie window should be separate values. It is quite reasonable to want + to configure a very short fail window (possibly 0) with a moderately long zombie window so that + connections can immediately start going upstream, but not very fast. + +* Failing an upstream should be more loosely connected to transactions. Currently there is a one + to one relationship where failure is defined as the failure of a specific transaction to connect. + There are situations where the number of connections attempts for mark a failure is should be + larger than the number of retries for a single transaction. For transiently busy upstreams and + low latency requests it can be reasonable to tune the per transaction timeout low with no retries + but this then risks marking down upstreams that were merely a bit slow at a given moment. + +* Parallel DNS requests should be supported. This is for both cross family requests and for split + DNS. + +History +======= + +This version has several major architectural changes from the previous version. + +* The data is split into records and info, not handled as a variant of a single data type. + +* Single and multiple address results are treated identically - a singleton is simply a multiple + of size 1. This was a major simplification of the implementation. + +* State information has been promoted to atomics and updates are immediate rather than scheduled. + +* Internal timing information is stored in ``std::chrono`` data types instead of internal types. +* Connections are throttled to dead upstreams, allowing only a single connection attempt per fail + window timing until a connection succeeds. +* The "resolve key" is now a separate data object from the HTTP request. This is a subtle but + major change. The effect is requests can be routed to different upstreams without changing + the request. Parent selection can be greatly simplified as it become merely a matter of setting + the resolve key, rather than having a completely different code path. diff --git a/doc/developer-guide/core-architecture/index.en.rst b/doc/developer-guide/core-architecture/index.en.rst index e88e35fb74e..97f59712d72 100644 --- a/doc/developer-guide/core-architecture/index.en.rst +++ b/doc/developer-guide/core-architecture/index.en.rst @@ -26,5 +26,6 @@ Core Architecture :maxdepth: 1 heap.en + hostdb.en rpc.en url_rewrite_architecture.en.rst From 330a20350f4cd4183729627622da8660a4f0b0b0 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Thu, 17 Jun 2021 09:41:17 -0500 Subject: [PATCH 21/33] Revert API change - maintain 9.x compatibility. --- example/plugins/c-api/protocol/TxnSM.c | 10 +++--- include/ts/ts.h | 5 ++- plugins/lua/ts_lua_misc.c | 13 +++---- src/traffic_server/InkAPI.cc | 49 +++++++++++++++++++------- 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/example/plugins/c-api/protocol/TxnSM.c b/example/plugins/c-api/protocol/TxnSM.c index cbd85122af3..cb7f00f44ec 100644 --- a/example/plugins/c-api/protocol/TxnSM.c +++ b/example/plugins/c-api/protocol/TxnSM.c @@ -477,8 +477,6 @@ int state_dns_lookup(TSCont contp, TSEvent event, TSHostLookupResult host_info) { TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); - struct sockaddr_storage q_server_addr; - struct sockaddr_in *addr; TSDebug(PLUGIN_NAME, "enter state_dns_lookup"); @@ -489,16 +487,16 @@ state_dns_lookup(TSCont contp, TSEvent event, TSHostLookupResult host_info) txn_sm->q_pending_action = NULL; /* Get the server IP from data structure TSHostLookupResult. */ - TSHostLookupResultAddrGet(host_info, (struct sockaddr *)&q_server_addr); + struct sockaddr const *sa = TSHostLookupResultAddrGet(host_info); /* Connect to the server using its IP. */ set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_connect_to_server); TSAssert(txn_sm->q_pending_action == NULL); - TSAssert(q_server_addr.ss_family == AF_INET); /* NO IPv6 in this plugin */ - addr = (struct sockaddr_in *)(&q_server_addr); + TSAssert(sa->sa_family == AF_INET); /* NO IPv6 in this plugin */ + struct sockaddr_in *addr = (struct sockaddr_in *)(sa); addr->sin_port = txn_sm->q_server_port; - txn_sm->q_pending_action = TSNetConnect(contp, (struct sockaddr const *)addr); + txn_sm->q_pending_action = TSNetConnect(contp, sa); return TS_SUCCESS; } diff --git a/include/ts/ts.h b/include/ts/ts.h index 03ff78c9914..624e13ff02a 100644 --- a/include/ts/ts.h +++ b/include/ts/ts.h @@ -1921,10 +1921,9 @@ tsapi TSAction TSHostLookup(TSCont contp, const char *hostname, size_t namelen); /** Retrieve an address from the host lookup. * * @param lookup_result Result handle passed to event callback. - * @param dst Destination for copying the address. - * @return @c TS_SUCCESS if the address was available and copied, @c TS_ERROR otherwise. + * @return A @c sockaddr with the address if successful, a @c nullptr if not. */ -tsapi TSReturnCode TSHostLookupResultAddrGet(TSHostLookupResult lookup_result, struct sockaddr *dst); +tsapi struct sockaddr const *TSHostLookupResultAddrGet(TSHostLookupResult lookup_result); /* TODO: Eventually, we might want something like this as well, but it requires support for building the HostDBInfo struct: diff --git a/plugins/lua/ts_lua_misc.c b/plugins/lua/ts_lua_misc.c index b3e29f8bffc..90216a63337 100644 --- a/plugins/lua/ts_lua_misc.c +++ b/plugins/lua/ts_lua_misc.c @@ -476,13 +476,14 @@ ts_lua_host_lookup_handler(TSCont contp, TSEvent event, void *edata) } else if (!edata) { lua_pushnil(L); } else { - struct sockaddr_storage addr; - TSHostLookupResult record = (TSHostLookupResult)edata; - TSHostLookupResultAddrGet(record, (struct sockaddr *)&addr); - if (addr.ss_family == AF_INET) { - inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)&addr)->sin_addr, cip, sizeof(cip)); + TSHostLookupResult record = (TSHostLookupResult)edata; + struct sockaddr const *addr = TSHostLookupResultAddrGet(record); + if (addr->sa_family == AF_INET) { + inet_ntop(AF_INET, &((struct sockaddr_in const *)&addr)->sin_addr, cip, sizeof(cip)); + } else if (addr->sa_family == AF_INET6) { + inet_ntop(AF_INET6, &((struct sockaddr_in6 const *)&addr)->sin6_addr, cip, sizeof(cip)); } else { - inet_ntop(AF_INET6, (const void *)&((struct sockaddr_in6 *)&addr)->sin6_addr, cip, sizeof(cip)); + cip[0] = 0; } lua_pushstring(L, cip); } diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc index a163be300fb..6a065a8c96e 100644 --- a/src/traffic_server/InkAPI.cc +++ b/src/traffic_server/InkAPI.cc @@ -7392,6 +7392,30 @@ TSNetAcceptNamedProtocol(TSCont contp, const char *protocol) } /* DNS Lookups */ +/// Context structure for the lookup callback to the plugin. +struct TSResolveInfo { + IpEndpoint addr; ///< Lookup result. + HostDBRecord *record = nullptr; ///< Record for the FQDN. +}; + +int +TSHostLookupTrampoline(TSCont contp, TSEvent ev, void *data) +{ + auto c = reinterpret_cast(contp); + // Set up the local context. + TSResolveInfo ri; + ri.record = static_cast(data); + if (ri.record) { + ri.record->rr_info()[0].data.ip.toSockAddr(ri.addr); + } + auto *target = reinterpret_cast(c->mdata); + // Deliver the message. + target->handleEvent(ev, &ri); + // Cleanup. + c->destroy(); + return TS_SUCCESS; +}; + TSAction TSHostLookup(TSCont contp, const char *hostname, size_t namelen) { @@ -7401,22 +7425,23 @@ TSHostLookup(TSCont contp, const char *hostname, size_t namelen) FORCE_PLUGIN_SCOPED_MUTEX(contp); - INKContInternal *i = (INKContInternal *)contp; - return (TSAction)hostDBProcessor.getbyname_re(i, hostname, namelen); + // There is no place to store the actual sockaddr to which a pointer should be returned. + // therefore an intermediate continuation is created to intercept the reply from HostDB. + // Its handler can create the required sockaddr context on the stack and then forward + // the event to the plugin continuation. The sockaddr cannot be placed in the HostDB + // record because that is a shared object. + auto bouncer = INKContAllocator.alloc(); + bouncer->m_event_func = &TSHostLookupTrampoline; + bouncer->mdata = contp; + return (TSAction)hostDBProcessor.getbyname_re(bouncer, hostname, namelen); } -TSReturnCode -TSHostLookupResultAddrGet(TSHostLookupResult lookup_result, sockaddr *dst) +sockaddr const * +TSHostLookupResultAddrGet(TSHostLookupResult lookup_result) { sdk_assert(sdk_sanity_check_hostlookup_structure(lookup_result) == TS_SUCCESS); - HostDBRecord::Handle record{reinterpret_cast(lookup_result)}; - if (record) { - auto info = record->rr_info(); - sdk_assert(info.size() > 0); - info[0].data.ip.toSockAddr(dst); - return TS_SUCCESS; - } - return TS_ERROR; + auto ri{reinterpret_cast(lookup_result)}; + return ri->addr.isValid() ? &ri->addr.sa : nullptr; } /* From 2b6529ffa592ad5aced5293e62f762b2f780270f Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Tue, 22 Jun 2021 10:30:05 -0500 Subject: [PATCH 22/33] Doc: update HostDB --- .../core-architecture/hostdb.en.rst | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/doc/developer-guide/core-architecture/hostdb.en.rst b/doc/developer-guide/core-architecture/hostdb.en.rst index 56ee7dbfaa5..cb3dbd62726 100644 --- a/doc/developer-guide/core-architecture/hostdb.en.rst +++ b/doc/developer-guide/core-architecture/hostdb.en.rst @@ -85,7 +85,10 @@ with upstream transactions and not sessions. ``ResolveInfo`` may contain a reference to a HostDB record, which preserves the record even if it is replaced due to DNS queries in other transactions. The record is not required as the resolution information can be supplied directly without DNS or HostDB, e.g. a plugin sets the upstream address -explicitly. +explicitly. The ``resolved_p`` flag indicates if the current information is valid and ready to be +used or not. A result of this is there is no longer a specific holder for API provided addresses - +the interface now puts the address in the ``ResolveInfo`` and marks it as resolved. This prevents +further DNS / HostDB lookups and the address is used as is. Issues ====== @@ -104,7 +107,7 @@ There is still some work to be done in future PRs. * The fail window and the zombie window should be separate values. It is quite reasonable to want to configure a very short fail window (possibly 0) with a moderately long zombie window so that - connections can immediately start going upstream, but not very fast. + probing connections can immediately start going upstream at a low rate. * Failing an upstream should be more loosely connected to transactions. Currently there is a one to one relationship where failure is defined as the failure of a specific transaction to connect. @@ -116,23 +119,31 @@ There is still some work to be done in future PRs. * Parallel DNS requests should be supported. This is for both cross family requests and for split DNS. +* It would be nice to be able to do the probing connections to an upstream using synthetic requests + instead of burning actual user requests. What would be needed is a handoff from ATS to the probe + to indicate a particular upstream is considered down, at which point active health checks are done + until the upstream is once again alive, at which point this is handed off back to ATS. + History ======= This version has several major architectural changes from the previous version. -* The data is split into records and info, not handled as a variant of a single data type. +* The data is split into records and info, not handled as a variant of a single data type. This + provides a noticeable simplification of the code. * Single and multiple address results are treated identically - a singleton is simply a multiple - of size 1. This was a major simplification of the implementation. - -* State information has been promoted to atomics and updates are immediate rather than scheduled. - -* Internal timing information is stored in ``std::chrono`` data types instead of internal types. + of size 1. This yeilds a major simplification of the implementation. * Connections are throttled to dead upstreams, allowing only a single connection attempt per fail window timing until a connection succeeds. +* Timing information is stored in ``std::chrono`` data types instead of proprietary types. + +* State information has been promoted to atomics and updates are immediate rather than scheduled. + This also means the data in the state machine is a reference to a shared object, not a local copy. + The promotion was necessary to coordinate zombie connections to dead upstreams across transactions. + * The "resolve key" is now a separate data object from the HTTP request. This is a subtle but major change. The effect is requests can be routed to different upstreams without changing the request. Parent selection can be greatly simplified as it become merely a matter of setting From 8b260f157ba0e6be9639bfc02f1c1f2610f84eea Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Tue, 22 Jun 2021 15:27:54 -0500 Subject: [PATCH 23/33] Fix: avoid reusing null hostent if the DNS query failed. --- iocore/hostdb/HostDB.cc | 79 ++++++++++++++++--------------- iocore/hostdb/P_HostDBProcessor.h | 2 +- proxy/http/HttpSM.cc | 1 + 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/iocore/hostdb/HostDB.cc b/iocore/hostdb/HostDB.cc index 90c3a995336..b02111d55d1 100644 --- a/iocore/hostdb/HostDB.cc +++ b/iocore/hostdb/HostDB.cc @@ -592,7 +592,7 @@ HostDBContinuation::refresh_hash() { Ptr old_bucket_mutex = hostDB.refcountcache->lock_for_key(hash.hash.fold()); // We're not pending DNS anymore. - remove_trigger_pending_dns(); + remove_and_trigger_pending_dns(); hash.refresh(); // Update the mutex if it's from the bucket. // Some call sites modify this after calling @c init so need to check. @@ -1137,7 +1137,6 @@ HostDBContinuation::dnsEvent(int event, HostEnt *e) if (failed && old_r && old_r->serve_stale_but_revalidate()) { r = old_r; serve_stale = true; - // Should return here? No point in doing initialization, it's the old data. } else if (is_byname()) { lookup_done(hash.host_name, ttl, failed ? nullptr : &e->srv_hosts, r); } else if (is_srv()) { @@ -1150,50 +1149,52 @@ HostDBContinuation::dnsEvent(int event, HostEnt *e) lookup_done(e->ent.h_name, ttl, &e->srv_hosts, r); } - auto rr_info = r->rr_info(); - // Fill in record type specific data. - if (is_srv()) { - char *pos = rr_info.rebind().end(); - SRV *q[valid_records]; - ink_assert(valid_records <= (int)hostdb_round_robin_max_count); - for (int i = 0; i < valid_records; ++i) { - q[i] = &e->srv_hosts.hosts[i]; - } - std::sort(q, q + valid_records, [](SRV *lhs, SRV *rhs) -> bool { return *lhs < *rhs; }); - - SRV **cur_srv = q; - for (auto &item : rr_info) { - auto t = *cur_srv++; // get next SRV record pointer. - memcpy(pos, t->host, t->host_len); // Append the name to the overall record. - item.assign(t, pos); - pos += t->host_len; - if (old_r) { // migrate as needed. - for (auto &old_item : old_r->rr_info()) { - if (item.data.srv.key == old_item.data.srv.key && 0 == strcmp(item.srvname(), old_item.srvname())) { - item.migrate_from(old_item); - break; + if (!failed) { // implies r != old_r + auto rr_info = r->rr_info(); + // Fill in record type specific data. + if (is_srv()) { + char *pos = rr_info.rebind().end(); + SRV *q[valid_records]; + ink_assert(valid_records <= (int)hostdb_round_robin_max_count); + for (int i = 0; i < valid_records; ++i) { + q[i] = &e->srv_hosts.hosts[i]; + } + std::sort(q, q + valid_records, [](SRV *lhs, SRV *rhs) -> bool { return *lhs < *rhs; }); + + SRV **cur_srv = q; + for (auto &item : rr_info) { + auto t = *cur_srv++; // get next SRV record pointer. + memcpy(pos, t->host, t->host_len); // Append the name to the overall record. + item.assign(t, pos); + pos += t->host_len; + if (old_r) { // migrate as needed. + for (auto &old_item : old_r->rr_info()) { + if (item.data.srv.key == old_item.data.srv.key && 0 == strcmp(item.srvname(), old_item.srvname())) { + item.migrate_from(old_item); + break; + } } } + // Archetypical example - "%zd" doesn't work on FreeBSD, "%ld" doesn't work on Ubuntu, "%lld" doesn't work on Fedora. + Debug_bw("dns_srv", "inserted SRV RR record [{}] into HostDB with TTL: {} seconds", t->host, ttl); } - // Archetypical example - "%zd" doesn't work on FreeBSD, "%ld" doesn't work on Ubuntu, "%lld" doesn't work on Fedora. - Debug_bw("dns_srv", "inserted SRV RR record [{}] into HostDB with TTL: {} seconds", t->host, ttl); - } - } else { // Otherwise this is a regular dns response - unsigned idx = 0; - for (auto &item : rr_info) { - item.assign(af, e->ent.h_addr_list[idx++]); - if (old_r) { // migrate as needed. - for (auto &old_item : old_r->rr_info()) { - if (item.data.ip == old_item.data.ip) { - item.migrate_from(old_item); - break; + } else { // Otherwise this is a regular dns response + unsigned idx = 0; + for (auto &item : rr_info) { + item.assign(af, e->ent.h_addr_list[idx++]); + if (old_r) { // migrate as needed. + for (auto &old_item : old_r->rr_info()) { + if (item.data.ip == old_item.data.ip) { + item.migrate_from(old_item); + break; + } } } } } } - if (!serve_stale) { + if (!serve_stale) { // implies r != old_r hostDB.refcountcache->put( r->key, r.get(), r->_record_size, (r->ip_timestamp + r->ip_timeout_interval + ts_seconds(hostdb_serve_stale_but_revalidate)).time_since_epoch().count()); @@ -1240,7 +1241,7 @@ HostDBContinuation::dnsEvent(int event, HostEnt *e) hostDB.pending_dns_for_hash(hash.hash).remove(this); // wake up everyone else who is waiting - remove_trigger_pending_dns(); + remove_and_trigger_pending_dns(); hostdb_cont_free(this); @@ -1395,7 +1396,7 @@ HostDBContinuation::set_check_pending_dns() } void -HostDBContinuation::remove_trigger_pending_dns() +HostDBContinuation::remove_and_trigger_pending_dns() { Queue &q = hostDB.pending_dns_for_hash(hash.hash); q.remove(this); diff --git a/iocore/hostdb/P_HostDBProcessor.h b/iocore/hostdb/P_HostDBProcessor.h index 637f3a2235e..9b71e22ab1f 100644 --- a/iocore/hostdb/P_HostDBProcessor.h +++ b/iocore/hostdb/P_HostDBProcessor.h @@ -280,7 +280,7 @@ struct HostDBContinuation : public Continuation { Ptr record = Ptr{}); int key_partition(); - void remove_trigger_pending_dns(); + void remove_and_trigger_pending_dns(); int set_check_pending_dns(); /** Optional values for @c init. diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc index 2a9419d3478..c006ece365f 100644 --- a/proxy/http/HttpSM.cc +++ b/proxy/http/HttpSM.cc @@ -2276,6 +2276,7 @@ HttpSM::process_hostdb_info(HostDBRecord *record) } else { // if use_client_target_addr is set, make sure the client addr is in the results pool t_state.dns_info.cta_validated_p = true; + t_state.dns_info.record = record; // Cache this but do not make it active. if (record->find(t_state.dns_info.addr) == nullptr) { SMDebug("http", "use_client_target_addr == 1. Client specified address is not in the pool, not validated."); t_state.dns_info.cta_validated_p = false; From aa376836b21e8332b84a815b1e778b674010a130 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Wed, 23 Jun 2021 08:51:25 -0500 Subject: [PATCH 24/33] Doc update. --- .../core-architecture/hostdb.en.rst | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/doc/developer-guide/core-architecture/hostdb.en.rst b/doc/developer-guide/core-architecture/hostdb.en.rst index cb3dbd62726..39cdc7689b8 100644 --- a/doc/developer-guide/core-architecture/hostdb.en.rst +++ b/doc/developer-guide/core-architecture/hostdb.en.rst @@ -90,6 +90,44 @@ used or not. A result of this is there is no longer a specific holder for API pr the interface now puts the address in the ``ResolveInfo`` and marks it as resolved. This prevents further DNS / HostDB lookups and the address is used as is. +Resolution Style +---------------- + +.. cpp:enum:: OS_Addr + + Metadata about the source of the resolved address.' + + .. cpp:enumerator:: TRY_DEFAULT + + Use default resolution. This is the initial state. + + .. cpp:enumerator:: TRY_HOSTDB + + Use HostDB to resolve the target key. + + .. cpp:enumerator:: TRY_CLIENT + + Use the client supplied target address. This is used for transparent connections - the upstream + address is obtained from the inbound connection. May fail over to HostDB. + + .. cpp:enumerator:: USE_HOSTDB + + Use HostDB to resolve the target key. + + .. cpp:enumerator:: USE_CLIENT + + Use the client supplied target address. + + .. cpp:enumerator:: USE_API + + Use the address provided via the plugin API. + + The parallel values for using HostDB and the client target address are to control fail over on + connection failure. The ``TRY_`` values can fail over to another style, but the ``USE_`` values + cannot. This prevents cycles of style changes by having any ``TRY_`` value fail over to a + ``USE_`` value, at which point it can no longer change. Note there is no ``TRY_API`` - if a + plugin sets the upstream address that is locked in. + Issues ====== From c6b0ae31006f4f08cf9b0f502d380ef550595a65 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Wed, 23 Jun 2021 08:56:21 -0500 Subject: [PATCH 25/33] Fix issue with plugin supplied address - suppress DNS lookup in that case. --- proxy/http/HttpTransact.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc index 960e649fdb7..5b19c81b1cf 100644 --- a/proxy/http/HttpTransact.cc +++ b/proxy/http/HttpTransact.cc @@ -1998,9 +1998,10 @@ HttpTransact::OSDNSLookup(State *s) // Should we skip the StartAccessControl()? why? if (ResolveInfo::OS_Addr::USE_CLIENT == s->dns_info.os_addr_style || - ResolveInfo::OS_Addr::USE_HOSTDB == s->dns_info.os_addr_style) { - // we've come back after already trying the server to get a better address - // and finished with all backtracking - return to trying the server. + ResolveInfo::OS_Addr::USE_HOSTDB == s->dns_info.os_addr_style || ResolveInfo::OS_Addr::USE_API == s->dns_info.os_addr_style) { + // we've come back after already trying the server to get a better address, + // or we're locked on a plugin supplied address. + // therefore no more backtracking - return to trying the server. TRANSACT_RETURN(how_to_open_connection(s), HttpTransact::HandleResponse); } else if (s->dns_info.lookup_name[0] <= '9' && s->dns_info.lookup_name[0] >= '0' && s->parent_params->parent_table->hostMatch && !s->http_config_param->no_dns_forward_to_parent) { From 6266b4f8238c6b9ce2f65223ca9322d90ae28590 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Wed, 23 Jun 2021 13:25:58 -0500 Subject: [PATCH 26/33] Fix brokenness in Lua do to API change and reversion. --- iocore/hostdb/I_HostDBProcessor.h | 10 ---------- plugins/lua/ts_lua_misc.c | 4 ++-- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/iocore/hostdb/I_HostDBProcessor.h b/iocore/hostdb/I_HostDBProcessor.h index bc3216fc768..065c6c3c5f1 100644 --- a/iocore/hostdb/I_HostDBProcessor.h +++ b/iocore/hostdb/I_HostDBProcessor.h @@ -114,16 +114,6 @@ char const *name_of(HostDBType t); struct HostDBInfo { using self_type = HostDBInfo; ///< Self reference type. - /// State of this upstream target. - enum Status : uint8_t { - /// Ready for connections, no failure. - ALIVE = 0, - /// Dead but past blackout time - limit to single connection attempts. - ZOMBIE = 1, - /// Dead, no connections allowed. - DEAD = 2, - }; - /// Default constructor. HostDBInfo() = default; diff --git a/plugins/lua/ts_lua_misc.c b/plugins/lua/ts_lua_misc.c index 90216a63337..1b83c2b73fd 100644 --- a/plugins/lua/ts_lua_misc.c +++ b/plugins/lua/ts_lua_misc.c @@ -479,9 +479,9 @@ ts_lua_host_lookup_handler(TSCont contp, TSEvent event, void *edata) TSHostLookupResult record = (TSHostLookupResult)edata; struct sockaddr const *addr = TSHostLookupResultAddrGet(record); if (addr->sa_family == AF_INET) { - inet_ntop(AF_INET, &((struct sockaddr_in const *)&addr)->sin_addr, cip, sizeof(cip)); + inet_ntop(AF_INET, &((struct sockaddr_in const *)addr)->sin_addr, cip, sizeof(cip)); } else if (addr->sa_family == AF_INET6) { - inet_ntop(AF_INET6, &((struct sockaddr_in6 const *)&addr)->sin6_addr, cip, sizeof(cip)); + inet_ntop(AF_INET6, &((struct sockaddr_in6 const *)addr)->sin6_addr, cip, sizeof(cip)); } else { cip[0] = 0; } From e03a668a211d6428d5e33b39a90c872af1e57918 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Wed, 23 Jun 2021 19:37:53 -0500 Subject: [PATCH 27/33] Revert CARP fix - not a good idea. --- iocore/hostdb/HostDB.cc | 6 ++---- proxy/http/HttpTransact.cc | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/iocore/hostdb/HostDB.cc b/iocore/hostdb/HostDB.cc index b02111d55d1..cf1f6e296c1 100644 --- a/iocore/hostdb/HostDB.cc +++ b/iocore/hostdb/HostDB.cc @@ -2361,13 +2361,11 @@ HostDBRecord::find(sockaddr const *addr) bool ResolveInfo::resolve_immediate() { - ts::LocalBufferWriter<256> bw; - if (resolved_p) { // nothing - already resolved. } else if (IpAddr tmp; TS_SUCCESS == tmp.load(lookup_name)) { - bw.print("[resolve_immediate] success - FQDN '{}' is a valid IP address.", lookup_name); - Debug("hostdb", "%.*s", int(bw.size()), bw.data()); + ts::bwprint(ts::bw_dbg, "[resolve_immediate] success - FQDN '{}' is a valid IP address.", lookup_name); + Debug("hostdb", "%s", ts::bw_dbg.c_str()); addr.assign(tmp); resolved_p = true; } diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc index 5b19c81b1cf..e1c6d20e04b 100644 --- a/proxy/http/HttpTransact.cc +++ b/proxy/http/HttpTransact.cc @@ -1998,7 +1998,7 @@ HttpTransact::OSDNSLookup(State *s) // Should we skip the StartAccessControl()? why? if (ResolveInfo::OS_Addr::USE_CLIENT == s->dns_info.os_addr_style || - ResolveInfo::OS_Addr::USE_HOSTDB == s->dns_info.os_addr_style || ResolveInfo::OS_Addr::USE_API == s->dns_info.os_addr_style) { + ResolveInfo::OS_Addr::USE_HOSTDB == s->dns_info.os_addr_style) { // we've come back after already trying the server to get a better address, // or we're locked on a plugin supplied address. // therefore no more backtracking - return to trying the server. From f25044d9a3a20ec96e166515f1a3aaf9445712b6 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Mon, 28 Jun 2021 17:34:03 -0500 Subject: [PATCH 28/33] Minor BWF tweak for better argument handling. --- include/tscore/BufferWriter.h | 4 ++-- src/tscore/unit_tests/test_BufferWriterFormat.cc | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/tscore/BufferWriter.h b/include/tscore/BufferWriter.h index 103af0b62b8..e255849d8cb 100644 --- a/include/tscore/BufferWriter.h +++ b/include/tscore/BufferWriter.h @@ -854,10 +854,10 @@ std::string & bwprintv(std::string &s, ts::TextView fmt, std::tuple const &args) { auto len = s.size(); // remember initial size - size_t n = ts::FixedBufferWriter(const_cast(s.data()), s.size()).printv(fmt, std::move(args)).extent(); + size_t n = ts::FixedBufferWriter(const_cast(s.data()), s.size()).printv(fmt, args).extent(); s.resize(n); // always need to resize - if shorter, must clip pre-existing text. if (n > len) { // dropped data, try again. - ts::FixedBufferWriter(const_cast(s.data()), s.size()).printv(fmt, std::move(args)); + ts::FixedBufferWriter(const_cast(s.data()), s.size()).printv(fmt, args); } return s; } diff --git a/src/tscore/unit_tests/test_BufferWriterFormat.cc b/src/tscore/unit_tests/test_BufferWriterFormat.cc index 90f95d899ea..3497523eae5 100644 --- a/src/tscore/unit_tests/test_BufferWriterFormat.cc +++ b/src/tscore/unit_tests/test_BufferWriterFormat.cc @@ -284,9 +284,11 @@ TEST_CASE("bwstring", "[bwprint][bwstring]") ts::bwprint(s, fmt, 99999, text); REQUIRE(s == "99999 -- e99a18c428cb38d5f260853678922e03"); + REQUIRE(strlen(s.c_str()) == text.size() + 9); ts::bwprint(s, "{} .. |{:,20}|", 32767, text); REQUIRE(s == "32767 .. |e99a18c428cb38d5f260|"); + REQUIRE(strlen(s.c_str()) == 31); ts::LocalBufferWriter<128> bw; char buff[128]; From 35e6d91c82fa36795befd615769475f14d5e1c3e Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Tue, 6 Jul 2021 08:32:24 -0500 Subject: [PATCH 29/33] Doc udpate. --- doc/developer-guide/core-architecture/hostdb.en.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/developer-guide/core-architecture/hostdb.en.rst b/doc/developer-guide/core-architecture/hostdb.en.rst index 39cdc7689b8..33eef3c253a 100644 --- a/doc/developer-guide/core-architecture/hostdb.en.rst +++ b/doc/developer-guide/core-architecture/hostdb.en.rst @@ -90,6 +90,9 @@ used or not. A result of this is there is no longer a specific holder for API pr the interface now puts the address in the ``ResolveInfo`` and marks it as resolved. This prevents further DNS / HostDB lookups and the address is used as is. +The upstream port is a bit tricky and should be cleaned up. Currently value in ``srv_port`` +determines the port if set. If not, then the port in ``addr`` is used. + Resolution Style ---------------- From 34518d1507831d1a62fbb2bf3344102c4d72a326 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Tue, 6 Jul 2021 08:33:06 -0500 Subject: [PATCH 30/33] HostDB: Fix plugin API port error. --- iocore/hostdb/I_HostDBProcessor.h | 6 +++--- proxy/http/HttpSM.cc | 1 - proxy/http/HttpTransact.cc | 2 ++ proxy/http/HttpTransact.h | 1 - 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/iocore/hostdb/I_HostDBProcessor.h b/iocore/hostdb/I_HostDBProcessor.h index 065c6c3c5f1..806a5667350 100644 --- a/iocore/hostdb/I_HostDBProcessor.h +++ b/iocore/hostdb/I_HostDBProcessor.h @@ -539,7 +539,8 @@ struct ResolveInfo { USE_API ///< Use the API provided address. }; - ResolveInfo() = default; + ResolveInfo() = default; + ~ResolveInfo() = default; /// Keep a reference to the base HostDB object, so it doesn't get GC'd. Ptr record; @@ -554,8 +555,7 @@ struct ResolveInfo { char const *lookup_name = nullptr; char srv_hostname[MAXDNAME] = {0}; const sockaddr *inbound_remote_addr = nullptr; ///< Remote address of inbound client - used for hashing. - // const sockaddr *client_target_addr = nullptr; ///< Set if trying a transparent connection. - in_port_t srv_port = 0; ///< Port from SRV lookup or API call. + in_port_t srv_port = 0; ///< Port from SRV lookup or API call. OS_Addr os_addr_style = OS_Addr::TRY_DEFAULT; HostResStyle host_res_style = HOST_RES_IPV4; diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc index c006ece365f..b88008f96ad 100644 --- a/proxy/http/HttpSM.cc +++ b/proxy/http/HttpSM.cc @@ -2263,7 +2263,6 @@ HttpSM::process_hostdb_info(HostDBRecord *record) NetVConnection *vc = ua_txn ? ua_txn->get_netvc() : nullptr; if (vc) { t_state.dns_info.set_upstream_address(vc->get_local_addr()); - t_state.dns_info.srv_port = ats_ip_port_cast(vc->get_local_addr()); t_state.dns_info.os_addr_style = ResolveInfo::OS_Addr::TRY_CLIENT; } } diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc index e1c6d20e04b..862c53f1fde 100644 --- a/proxy/http/HttpTransact.cc +++ b/proxy/http/HttpTransact.cc @@ -1937,6 +1937,8 @@ HttpTransact::OSDNSLookup(State *s) // If the SRV response has a port number, we should honor it. Otherwise we do the port defined in remap if (s->dns_info.resolved_p && s->dns_info.srv_port) { s->server_info.dst_addr.port() = htons(s->dns_info.srv_port); + } else if (s->dns_info.os_addr_style == ResolveInfo::OS_Addr::USE_API && 0 != ats_ip_port_cast(s->dns_info.addr)) { + // Nothing - port set via API and already copied over. } else { s->server_info.dst_addr.port() = htons(s->hdr_info.client_request.port_get()); // now we can set the port. } diff --git a/proxy/http/HttpTransact.h b/proxy/http/HttpTransact.h index 6da2385aac9..a46d5879299 100644 --- a/proxy/http/HttpTransact.h +++ b/proxy/http/HttpTransact.h @@ -830,7 +830,6 @@ class HttpTransact url_map.clear(); arena.reset(); unmapped_url.clear(); - // hostdb_entry.clear(); dns_info.~ResolveInfo(); outbound_conn_track_state.clear(); From 882c362b673723ded6121b111ad6c3fe5286ca38 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Tue, 20 Jul 2021 15:52:53 -0500 Subject: [PATCH 31/33] Fix missing mutex on TSHostLookup bounce continuation. --- src/traffic_server/InkAPI.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc index 6a065a8c96e..020532f82dd 100644 --- a/src/traffic_server/InkAPI.cc +++ b/src/traffic_server/InkAPI.cc @@ -7433,6 +7433,7 @@ TSHostLookup(TSCont contp, const char *hostname, size_t namelen) auto bouncer = INKContAllocator.alloc(); bouncer->m_event_func = &TSHostLookupTrampoline; bouncer->mdata = contp; + bouncer->mutex = reinterpret_cast(contp)->mutex; return (TSAction)hostDBProcessor.getbyname_re(bouncer, hostname, namelen); } From f694770b35a7d225333526f00680acdfaa8f4f9c Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Tue, 27 Jul 2021 16:54:44 -0500 Subject: [PATCH 32/33] Fix. --- src/traffic_server/InkAPI.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc index 020532f82dd..5e9b2ab3ed1 100644 --- a/src/traffic_server/InkAPI.cc +++ b/src/traffic_server/InkAPI.cc @@ -7430,10 +7430,9 @@ TSHostLookup(TSCont contp, const char *hostname, size_t namelen) // Its handler can create the required sockaddr context on the stack and then forward // the event to the plugin continuation. The sockaddr cannot be placed in the HostDB // record because that is a shared object. - auto bouncer = INKContAllocator.alloc(); - bouncer->m_event_func = &TSHostLookupTrampoline; - bouncer->mdata = contp; - bouncer->mutex = reinterpret_cast(contp)->mutex; + auto bouncer = INKContAllocator.alloc(); + bouncer->init(&TSHostLookupTrampoline, reinterpret_cast(reinterpret_cast(contp)->mutex.get())); + bouncer->mdata = contp; return (TSAction)hostDBProcessor.getbyname_re(bouncer, hostname, namelen); } From 3df947f8084c75175ccf102a91c30d043a22be1e Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Thu, 26 Aug 2021 16:34:51 -0500 Subject: [PATCH 33/33] Fix nullptr dereference for SRV records. --- iocore/hostdb/I_HostDBProcessor.h | 7 +++++++ proxy/http/HttpSM.cc | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/iocore/hostdb/I_HostDBProcessor.h b/iocore/hostdb/I_HostDBProcessor.h index 806a5667350..74abb44b549 100644 --- a/iocore/hostdb/I_HostDBProcessor.h +++ b/iocore/hostdb/I_HostDBProcessor.h @@ -618,6 +618,8 @@ struct ResolveInfo { /// Select / resolve to the next RR entry for the record. bool select_next_rr(); + + bool is_srv() const; }; /** The Host Database access interface. */ @@ -832,6 +834,11 @@ ResolveInfo::set_upstream_port(in_port_t port) srv_port = port; } +inline bool +ResolveInfo::is_srv() const +{ + return record && record->is_srv(); +} // --- void run_HostDBTest(); diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc index b88008f96ad..19c9ad27567 100644 --- a/proxy/http/HttpSM.cc +++ b/proxy/http/HttpSM.cc @@ -2331,9 +2331,9 @@ HttpSM::state_hostdb_lookup(int event, void *data) pending_action = nullptr; process_srv_info(static_cast(data)); - char const *host_name = t_state.dns_info.record->is_srv() ? t_state.dns_info.record->name() : t_state.dns_info.lookup_name; + char const *host_name = t_state.dns_info.is_srv() ? t_state.dns_info.record->name() : t_state.dns_info.lookup_name; HostDBProcessor::Options opt; - opt.port = t_state.dns_info.record->is_srv() ? t_state.dns_info.srv_port : t_state.server_info.dst_addr.host_order_port(); + opt.port = t_state.dns_info.is_srv() ? t_state.dns_info.srv_port : t_state.server_info.dst_addr.host_order_port(); opt.flags = (t_state.cache_info.directives.does_client_permit_dns_storing) ? HostDBProcessor::HOSTDB_DO_NOT_FORCE_DNS : HostDBProcessor::HOSTDB_FORCE_DNS_RELOAD; opt.timeout = (t_state.api_txn_dns_timeout_value != -1) ? t_state.api_txn_dns_timeout_value : 0;