// The Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2020 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. #include netdb_enable; extern conf::item netdb_internal; extern const std::map, uint16_t> service_ports; extern const std::map, string_view> service_names; } /// Custom internal database. This translates a service name and protocol /// into a port number. Note that a query to this table will only be made /// after the system query does not return results (or cannot be made). [[gnu::visibility("internal")]] decltype(ircd::net::dns::service_ports) ircd::net::dns::service_ports { { { "dns", "tcp" }, 53 }, { { "http", "tcp" }, 80 }, { { "https", "tcp" }, 443 }, { { "matrix", "tcp" }, 8448 }, }; /// Custom internal database. This translates a service port and protocol /// into a service name. Note that a query to this table will only be made /// after the system query does not return results (or cannot be made). [[gnu::visibility("internal")]] decltype(ircd::net::dns::service_names) ircd::net::dns::service_names { { { 53, "tcp" }, "dns" }, { { 80, "tcp" }, "http" }, { { 443, "tcp" }, "https" }, { { 8448, "tcp" }, "matrix" }, }; [[gnu::visibility("internal")]] decltype(ircd::net::dns::netdb_enable) ircd::net::dns::netdb_enable { { "name", "ircd.net.dns.netdb.enable" }, { "default", true }, }; [[gnu::visibility("internal")]] decltype(ircd::net::dns::netdb_internal) ircd::net::dns::netdb_internal { { "name", "ircd.net.dns.netdb.internal" }, { "default", true }, }; void ircd::net::dns::init::service_init() { static const int stay_open {true}; if(netdb_enable) { #ifdef HAVE_NETDB_H const mods::ldso::exceptions enable {false}; ::setservent(stay_open); netdb_ready = true; #endif } } [[gnu::cold]] void ircd::net::dns::init::service_fini() noexcept { if(std::exchange(netdb_ready, false)) { #ifdef HAVE_NETDB_H const mods::ldso::exceptions enable {false}; ::endservent(); #endif } } uint16_t ircd::net::dns::service_port(const string_view &name, const string_view &prot) { const auto ret { service_port(std::nothrow, name, prot) }; if(unlikely(!ret)) throw error { "Port for service %s:%s not found", name, prot?: "*"_sv, }; return ret; } #ifdef HAVE_NETDB_H uint16_t ircd::net::dns::service_port(std::nothrow_t, const string_view &name, const string_view &prot) try { thread_local struct ::servent res, *ent {nullptr}; thread_local char _name[32], _prot[32], buf[2048]; if(likely(netdb_internal)) if((res.s_port = _service_port(name, prot))) return res.s_port; const mods::ldso::exceptions enable {false}; const prof::syscall_usage_warning timer { "net::dns::service_port(%s)", name }; strlcpy(_name, name); strlcpy(_prot, prot); if(likely(netdb_ready)) syscall ( ::getservbyname_r, _name, prot? _prot : nullptr, &res, buf, sizeof(buf), &ent ); assert(!ent || ent->s_port != 0); assert(!ent || name == ent->s_name); assert(!ent || !prot || prot == ent->s_proto); if(!ent || !ent->s_port) if((res.s_port = _service_port(name, prot))) return res.s_port; if(unlikely(!ent || !ent->s_port)) log::error { log, "Uknown service %s/%s; please add port number to /etc/services", name, prot?: "*"_sv }; return ent? htons(ent->s_port): 0U; } catch(const std::exception &e) { log::critical { log, "Failure when translating service %s:%s to port number :%s", name, prot?: "*"_sv, e.what(), }; throw; } #else uint16_t ircd::net::dns::service_port(std::nothrow_t, const string_view &name, const string_view &prot) { return _service_port(name, prot); } #endif ircd::string_view ircd::net::dns::service_name(const mutable_buffer &out, const uint16_t &port, const string_view &prot) { const auto ret { service_name(std::nothrow, out, port, prot) }; if(unlikely(!ret)) throw error { "Name of service for port %u:%s not found", port, prot?: "*"_sv, }; return ret; } #ifdef HAVE_NETDB_H ircd::string_view ircd::net::dns::service_name(std::nothrow_t, const mutable_buffer &out, const uint16_t &port, const string_view &prot) try { thread_local struct ::servent res, *ent {nullptr}; thread_local char _prot[32], buf[2048]; if(likely(netdb_internal)) { string_view ret; if((ret = strlcpy(out, _service_name(port, prot)))) return ret; } const mods::ldso::exceptions enable {false}; const prof::syscall_usage_warning timer { "net::dns::service_name(%u)", port }; strlcpy(_prot, prot); if(likely(netdb_ready)) syscall ( ::getservbyport_r, ntohs(port), prot? _prot : nullptr, &res, buf, sizeof(buf), &ent ); assert(!ent || ent->s_port == ntohs(port)); assert(!ent || !prot || prot == ent->s_proto); return ent? strlcpy(out, ent->s_name): strlcpy(out, _service_name(port, prot)); } catch(const std::exception &e) { log::critical { log, "Failure when translating port %u:%s to service name :%s", port, prot?: "*"_sv, e.what(), }; throw; } #else ircd::string_view ircd::net::dns::service_name(std::nothrow_t, const mutable_buffer &out, const uint16_t &port, const string_view &prot) { return strlcpy(out, _service_name(port, prot)); } #endif uint16_t ircd::net::dns::_service_port(const string_view &name, const string_view &prot) { const pair query { name, prot }; const auto it { service_ports.find(query) }; return it != end(service_ports)? it->second: 0; } ircd::string_view ircd::net::dns::_service_name(const uint16_t &port, const string_view &prot) { const pair query { port, prot }; const auto it { service_names.find(query) }; return it != end(service_names)? it->second: string_view{}; }