| /** @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; |
| } |