blob: 32d3823e8ee47831797850325d09334503b043aa [file]
/** @file
HostFile class for processing a hosts file
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "swoc/swoc_file.h"
#include "iocore/hostdb/HostFile.h"
#include "P_HostDBProcessor.h"
/* 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::string_view, std::tuple<std::vector<IpAddr>, std::vector<IpAddr>>>;
using AddrHostMap = std::unordered_map<IpAddr, std::string_view, IpAddr::Hasher>;
namespace
{
constexpr unsigned IPV4_IDX = 0;
constexpr unsigned IPV6_IDX = 1;
} // namespace
static void
ParseHostLine(swoc::TextView line, HostAddrMap &map, AddrHostMap &rmap)
{
// Elements should be the address then a list of host names.
swoc::TextView addr_text = line.take_prefix_if(&isspace);
IpAddr addr;
// Don't use RecHttpLoadIp because the address *must* be literal.
if (TS_SUCCESS != addr.load(addr_text)) {
return;
}
while (!line.ltrim_if(&isspace).empty()) {
swoc::TextView name = line.take_prefix_if(&isspace);
if (addr.isIp6()) {
std::get<IPV6_IDX>(map[name]).push_back(addr);
} else if (addr.isIp4()) {
std::get<IPV4_IDX>(map[name]).push_back(addr);
}
// use insert here so only the first mapping in the file is used to provide a stable view between reloads
rmap.insert(std::pair(addr, name));
}
}
HostDBRecord::Handle
HostFile::lookup(const HostDBHash &hash)
{
HostDBRecord::Handle result;
// 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 spot = forward.find(hash.host_name); spot != forward.end()) {
result = (hash.db_mark == HOSTDB_MARK_IPV4) ? spot->second.record_4 : spot->second.record_6;
}
} else if (hash.db_mark != HOSTDB_MARK_SRV) {
if (auto spot = reverse.find(hash.ip); spot != reverse.end()) {
result = spot->second;
}
}
return result;
}
std::shared_ptr<HostFile>
ParseHostFile(swoc::file::path const &path, ts_seconds interval)
{
std::shared_ptr<HostFile> hf;
static DbgCtl dbg_ctl{"hostdb"};
Dbg_bw(dbg_ctl, R"(Loading host file "{}")", path.view());
if (!path.empty()) {
std::error_code ec;
std::string content = swoc::file::load(path, ec);
if (!ec) {
HostAddrMap addr_map;
AddrHostMap host_map;
swoc::TextView text{content};
while (text) {
auto line = text.take_prefix_at('\n').ltrim_if(&isspace);
if (line.empty() || '#' == *line) {
continue;
}
ParseHostLine(line, addr_map, host_map);
}
// @a map should be loaded with all of the data, create the records.
hf = std::make_shared<HostFile>(interval);
// Common loading function for creating a record from the address vector.
auto loader = [](swoc::TextView key, std::vector<IpAddr> const &v) -> HostDBRecord::Handle {
HostDBRecord::Handle record{HostDBRecord::alloc(key, v.size())};
record->af_family = v.front().family(); // @a v is presumed family homogeneous
auto rr_info = record->rr_info();
auto spot = v.begin();
for (auto &item : rr_info) {
item.assign(*spot++);
}
return record;
};
// 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.
// 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<IPV4_IDX>(value); v.size() > 0) {
auto r = loader(key, v);
hf->forward[r->name_view()].record_4 = r;
}
// IPv6
if (auto const &v = std::get<IPV6_IDX>(value); v.size() > 0) {
auto r = loader(key, v);
hf->forward[r->name_view()].record_6 = r;
}
}
// Walk the temporary reverse map and create the reverse index.
for (auto const &[ip, host] : host_map) {
auto lup = hf->forward.find(host);
if (lup != hf->forward.end()) {
if (ip.isIp4()) {
hf->reverse[ip] = lup->second.record_4;
} else {
hf->reverse[ip] = lup->second.record_6;
}
}
}
}
}
return hf;
}