14 Commits

Author SHA1 Message Date
c56cc6edbd Version bump to 1.1.3 2023-10-08 09:27:38 +02:00
5c74de5685 Switch a lot of Strings to Arc<str> 2023-10-08 09:12:06 +02:00
223abdd804 cargo update
This one only removed dependencies 🥳
2023-10-08 08:09:44 +02:00
639d4579e9 Fixed the url pointing to the google dns information^W marketing 2023-08-08 23:15:43 +02:00
4b3a8d5e08 Fixed dns resolver information templates 2023-08-08 23:06:55 +02:00
53da9023da cargo update 2023-08-07 21:59:25 +02:00
4876fb7ea0 Updated README 2023-08-07 21:58:42 +02:00
2aa6baaa57 Removed dns search functionality completely
I don't see it working and not being annoying
2023-08-07 21:52:31 +02:00
daa68bbd5d Reenabled search prevention fqdn dot 2023-08-07 21:42:56 +02:00
231e46a688 Disabled domain search as it caused problems 2023-08-07 21:40:22 +02:00
2fe1b69174 Beeter communication of dns errors 2023-08-07 21:09:14 +02:00
2e1f6a77ac Slightly better handling of invalid domain names 2023-08-07 20:05:25 +02:00
1fe59d24d5 Discard additional responses instead of misattributing them 2023-08-07 19:03:18 +02:00
51d7954d71 Make the sitename render as inline-block to prevent accidental clicking 2023-08-06 13:23:41 +02:00
15 changed files with 367 additions and 409 deletions

486
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package]
name = "echoip-slatecave"
version = "0.1.0"
version = "1.1.3"
edition = "2021"
authors = ["Slatian <baschdel@disroot.org>"]
@ -16,7 +16,7 @@ idna = "0.3"
lazy_static = "1.4.0"
parking_lot = "0.12"
regex = "1.7"
serde = { version = "1", features = ["derive"] }
serde = { version = "1", features = ["derive","rc"] }
tokio = { version = "1", features = ["macros","signal"] }
tera = "1"
toml = "0.7"

View File

@ -72,7 +72,7 @@ This configuration option will not be exposed over the webinterface.
By default echoip-slatecave uses the system configuration for dns like most other programs.
In case this is undesired one can difable it by setting `enable_system_resolver` to false.
In case this is undesired one can disable it by setting `enable_system_resolver` to false.
```toml
[dns]
@ -90,10 +90,6 @@ In case you want to use the system resolver and customize it.
`system_resolver_weight`
: Equivalent to the `weight` of a custom resolver, default: 1000
### `search`
This is for a work in progress feature that allows confiuring search domains for all custom dns resolvers.
### Custom resolvers
It is possible to confgure custom resolvers in plce of or in addition to the default system resolver.

View File

@ -67,9 +67,9 @@ The default configuration is pretty liberal so that the average human probably w
## TODO
* [ ] Investigate why search isn't working for global TLDs
* [ ] Add a way to configure just the dns server addresses and derive the port from the protocol.
* [ ] Add an about page for the system resolver
* [ ] Expose DNS responses from the additional on the web interface
## License

View File

@ -28,9 +28,6 @@ allow_reverse_lookup = true
# that end with one of these suffixes don't exist
hidden_suffixes = [".com"]
# doesn't really work 🙁
search = ["org","net"]
[geoip]
# Path to geoip databses
# Currently only the mmdb format is supported
@ -116,7 +113,7 @@ tls_dns_name = "cloudflare-dns.com"
[dns.resolver.google]
display_name = "Google"
info_url = "https://www.cloudflare.com/dns/"
info_url = "https://developers.google.com/speed/public-dns/docs/using"
aliases = ["goo","8888"]
weight = 440

View File

@ -1,7 +1,7 @@
use serde::{Deserialize,Serialize};
use trust_dns_resolver::config::Protocol;
use trust_dns_resolver::Name;
use std::sync::Arc;
use std::collections::HashMap;
use std::net::SocketAddr;
@ -11,13 +11,12 @@ pub struct DnsConfig {
pub allow_forward_lookup: bool,
pub allow_reverse_lookup: bool,
pub hidden_suffixes: Vec<String>,
pub search: Vec<String>,
pub resolver: HashMap<String,DnsResolverConfig>,
pub resolver: HashMap<Arc<str>,DnsResolverConfig>,
pub enable_system_resolver: bool,
pub system_resolver_name: String,
pub system_resolver_name: Arc<str>,
pub system_resolver_weight: i32,
pub system_resolver_id: String,
pub system_resolver_id: Arc<str>,
}
#[derive(Deserialize, Serialize, Clone)]
@ -32,18 +31,16 @@ pub enum DnsProtocol {
#[derive(Deserialize, Serialize, Clone)]
pub struct DnsResolverConfig {
pub display_name: String,
pub display_name: Arc<str>,
#[serde(default)]
pub info_url: Option<String>,
pub info_url: Option<Arc<str>>,
#[serde(default)]
pub aliases: Vec<String>,
pub aliases: Vec<Arc<str>>,
#[serde(default="zero")]
pub weight: i32,
pub servers: Vec<SocketAddr>,
#[serde(default)]
pub search: Vec<String>,
pub protocol: DnsProtocol,
pub tls_dns_name: Option<String>,
pub tls_dns_name: Option<Arc<str>>,
#[serde(skip_serializing)] //Don't leak our bind address to the outside
pub bind_address: Option<SocketAddr>,
#[serde(default="default_true")]
@ -65,12 +62,11 @@ impl Default for DnsConfig {
allow_reverse_lookup: false,
hidden_suffixes: Vec::new(),
resolver: Default::default(),
search: Vec::new(),
enable_system_resolver: true,
system_resolver_name: "System".to_string(),
system_resolver_name: "System".into(),
system_resolver_weight: 1000,
system_resolver_id: "system".to_string(),
system_resolver_id: "system".into(),
}
}
}
@ -89,30 +85,23 @@ impl Into<Protocol> for DnsProtocol {
impl DnsResolverConfig {
pub fn to_trust_resolver_config(
&self,
additional_search: &Vec<String>,
&self
) -> trust_dns_resolver::config::ResolverConfig {
let mut resolver = trust_dns_resolver::config::ResolverConfig::new();
for server in &self.servers {
resolver.add_name_server(trust_dns_resolver::config::NameServerConfig{
socket_addr: *server,
protocol: self.protocol.clone().into(),
tls_dns_name: self.tls_dns_name.clone(),
tls_dns_name: self.tls_dns_name.clone().map(|s| s.to_string()),
trust_nx_responses: self.trust_nx_responses,
tls_config: None,
bind_addr: self.bind_address,
});
}
for search in &self.search {
if let Ok(name) = Name::from_str_relaxed(search) {
resolver.add_search(name);
}
}
for search in additional_search {
if let Ok(name) = Name::from_str_relaxed(search) {
resolver.add_search(name);
}
}
// Not configuring domain search here because searching
// on the resolver level is a bad idea unless we are
// taling about the system resolver which we
// can't tell what to do (which is good!)
return resolver;
}
}

View File

@ -22,6 +22,7 @@ use serde::{Deserialize,Serialize};
use tera::Tera;
use tower::ServiceBuilder;
use tower_http::services::ServeDir;
use trust_dns_resolver::Name;
use trust_dns_resolver::TokioAsyncResolver;
use tokio::signal::unix::{
@ -75,26 +76,26 @@ pub struct IpResult {
asn: Option<AsnResult>,
location: Option<LocationResult>,
ip_info: AddressInfo,
used_dns_resolver: Option<String>,
used_dns_resolver: Option<Arc<str>>,
}
// We need this one to hide the partial lookup field when irelevant
pub fn not(b: &bool) -> bool { !b }
#[derive(Serialize, Default, Clone)]
#[derive(Serialize, Clone)]
pub struct DigResult {
records: simple_dns::DnsLookupResult,
#[serde(skip_serializing_if = "IdnaName::was_ascii")]
idn: IdnaName,
#[serde(skip_serializing_if = "not")]
partial_lookup: bool,
used_dns_resolver: String,
used_dns_resolver: Arc<str>,
}
struct ServiceSharedState {
templating_engine: templating_engine::Engine,
dns_resolvers: HashMap<String,TokioAsyncResolver>,
dns_resolver_aliases: HashMap<String,String>,
dns_resolvers: HashMap<Arc<str>,TokioAsyncResolver>,
dns_resolver_aliases: HashMap<Arc<str>,Arc<str>>,
asn_db: geoip::MMDBCarrier,
location_db: geoip::MMDBCarrier,
config: config::EchoIpServiceConfig,
@ -104,7 +105,7 @@ struct ServiceSharedState {
#[derive(Clone)]
struct DerivedConfiguration {
dns_resolver_selectables: Vec<Selectable>,
default_resolver: String,
default_resolver: Arc<str>,
}
#[derive(Parser)]
@ -233,8 +234,8 @@ async fn main() {
println!("Initalizing dns resolvers ...");
let mut dns_resolver_selectables = Vec::<Selectable>::new();
let mut dns_resolver_map: HashMap<String,TokioAsyncResolver> = HashMap::new();
let mut dns_resolver_aliases: HashMap<String,String> = HashMap::new();
let mut dns_resolver_map: HashMap<Arc<str>,TokioAsyncResolver> = HashMap::new();
let mut dns_resolver_aliases: HashMap<Arc<str>,Arc<str>> = HashMap::new();
if config.dns.enable_system_resolver {
println!("Initalizing System resolver ...");
@ -258,7 +259,7 @@ async fn main() {
for (key, resolver_config) in &config.dns.resolver {
println!("Initalizing {} resolver ...", key);
let resolver = TokioAsyncResolver::tokio(
resolver_config.to_trust_resolver_config(&config.dns.search),
resolver_config.to_trust_resolver_config(),
Default::default()
).unwrap();
dns_resolver_map.insert(key.clone(), resolver);
@ -289,7 +290,7 @@ async fn main() {
dns_resolver_selectables.sort_by(|a,b| b.weight.cmp(&a.weight));
let default_resolver = dns_resolver_selectables.get(0)
.map(|s| s.id.clone() )
.unwrap_or("none".to_string());
.unwrap_or("none".into());
let derived_config = DerivedConfiguration {
dns_resolver_selectables: dns_resolver_selectables,
default_resolver: default_resolver,
@ -363,10 +364,10 @@ async fn settings_query_middleware<B>(
let mut dns_resolver_id = derived_config.default_resolver;
if let Some(resolver_id) = query.dns {
dns_resolver_id = resolver_id;
dns_resolver_id = resolver_id.into();
} else if let Some(cookie_header) = cookie_header {
if let Some(resolver_id) = cookie_header.0.get("dns_resolver") {
dns_resolver_id = resolver_id.to_string();
dns_resolver_id = resolver_id.into();
}
}
@ -447,7 +448,7 @@ async fn handle_default_route(
}
}
let result = get_ip_result(&address, &settings.lang, &"default".to_string(), &state).await;
let result = get_ip_result(&address, &settings.lang, &"default".into(), &state).await;
let user_agent: Option<String> = match user_agent_header {
Some(TypedHeader(user_agent)) => Some(user_agent.to_string()),
@ -492,11 +493,11 @@ async fn handle_search_request(
}
if let Some(via_cap) = VIA_REGEX.captures(&search_query) {
if let Some(via) = via_cap.get(1).map(|c| c.as_str().to_string()) {
if let Some(via) = via_cap.get(1) {
let state = Arc::clone(&arc_state);
if state.dns_resolvers.contains_key(&via) {
settings.dns_resolver_id = via;
} else if let Some(alias) = state.dns_resolver_aliases.get(&via) {
if state.dns_resolvers.contains_key(via.as_str()) {
settings.dns_resolver_id = via.as_str().into();
} else if let Some(alias) = state.dns_resolver_aliases.get(via.as_str()) {
settings.dns_resolver_id = alias.clone();
}
}
@ -536,7 +537,7 @@ async fn handle_dns_resolver_route_with_path(
extract::Path(query): extract::Path<String>,
) -> Response {
let state = Arc::clone(&arc_state);
if let Some(resolver) = state.config.dns.resolver.get(&query) {
if let Some(resolver) = state.config.dns.resolver.get(query.as_str()) {
state.templating_engine.render_view(
&settings,
&View::DnsResolver{ config: resolver.clone() },
@ -583,7 +584,7 @@ async fn handle_ip_request(
async fn get_ip_result(
address: &IpAddr,
lang: &String,
dns_resolver_name: &String,
dns_resolver_name: &Arc<str>,
state: &ServiceSharedState,
) -> IpResult {
@ -604,7 +605,7 @@ async fn get_ip_result(
// do reverse lookup
let mut hostname: Option<String> = None;
let mut used_dns_resolver: Option<String> = None;
let mut used_dns_resolver: Option<Arc<str>> = None;
if state.config.dns.allow_reverse_lookup {
if let Some(dns_resolver) = &state.dns_resolvers.get(dns_resolver_name) {
hostname = simple_dns::reverse_lookup(&dns_resolver, &address).await;
@ -672,32 +673,33 @@ async fn handle_dig_request(
async fn get_dig_result(
dig_query: &String,
dns_resolver_name: &String,
dns_resolver_name: &Arc<str>,
state: &ServiceSharedState,
do_full_lookup: bool,
) -> DigResult {
let name = &dig_query.trim().trim_end_matches(".").to_string();
let idna_name = IdnaName::from_string(&name);
if let Some(dns_resolver) = &state.dns_resolvers.get(dns_resolver_name) {
if let Some(dns_resolver) = state.dns_resolvers.get(dns_resolver_name) {
if let Ok(domain_name) = Name::from_str_relaxed(name.to_owned()+".") {
if match_domain_hidden_list(&name, &state.config.dns.hidden_suffixes) {
// Try to hide the fact that we didn't do dns resolution at all
// We resolve example.org as basic avoidance of timing sidechannels.
// WARNING: this timing sidechannel avoidance is very crude.
simple_dns::lookup(
&dns_resolver,
&("example.org.".to_string()),
&Name::from_ascii("example.org.").expect("Static Dummy Name"),
do_full_lookup).await;
DigResult {
return DigResult {
records: DnsLookupResult{ nxdomain: true , ..Default::default() },
idn: idna_name,
partial_lookup: !do_full_lookup,
used_dns_resolver: dns_resolver_name.clone(),
}
} else {
DigResult {
return DigResult {
records: simple_dns::lookup(
&dns_resolver,
&(idna_name.idn.clone().unwrap_or(name.to_owned())+"."),
&domain_name,
do_full_lookup).await,
idn: idna_name,
partial_lookup: !do_full_lookup,
@ -705,6 +707,27 @@ async fn get_dig_result(
}
}
} else {
return Default::default();
// Invalid domain name
return DigResult {
records: DnsLookupResult{
invalid_name: true,
.. Default::default()
},
idn: idna_name,
partial_lookup: !do_full_lookup,
used_dns_resolver: dns_resolver_name.clone(),
}
}
} else {
// Unknown resolver name
return DigResult {
records: DnsLookupResult{
unkown_resolver: true,
.. Default::default()
},
idn: idna_name,
partial_lookup: !do_full_lookup,
used_dns_resolver: "unkown_resolver".into(),
}
}
}

View File

@ -1,5 +1,7 @@
use serde::{Deserialize,Serialize};
use std::sync::Arc;
/* Response format */
#[derive(Deserialize, Serialize, Clone, Copy)]
@ -39,13 +41,13 @@ pub struct QuerySettings {
pub format: ResponseFormat,
pub lang: String,
pub available_dns_resolvers: Vec<Selectable>,
pub dns_resolver_id: String,
pub dns_resolver_id: Arc<str>,
}
#[derive(Deserialize, Serialize, Clone)]
pub struct Selectable {
pub id: String,
pub name: String,
pub id: Arc<str>,
pub name: Arc<str>,
pub weight: i32,
}

View File

@ -15,6 +15,7 @@ use trust_dns_resolver::{
error::ResolveError,
error::ResolveErrorKind,
lookup::Lookup,
Name,
TokioAsyncResolver,
};
@ -41,6 +42,8 @@ pub struct DnsLookupResult {
pub dns_error: bool,
pub nxdomain: bool,
pub timeout: bool,
pub invalid_name: bool,
pub unkown_resolver: bool,
}
#[derive(serde::Deserialize, serde::Serialize, Clone, PartialEq)]
@ -171,8 +174,14 @@ pub fn integrate_lookup_result(dig_result: &mut DnsLookupResult, lookup_result:
RecordType::TXT => set_default_if_none(&mut dig_result.txt),
_ => { /* This should not happen */ },
};
for record in lookup.iter() {
add_record_to_lookup_result(dig_result, record);
let name = lookup.query().name();
for record in lookup.record_iter() {
if name == record.name() {
if let Some(data) = record.data() {
add_record_to_lookup_result(dig_result, data);
}
}
//TODO: handle additional responses
}
},
Err(e) => {
@ -210,7 +219,7 @@ pub fn integrate_lookup_result(dig_result: &mut DnsLookupResult, lookup_result:
// records will be fetched.
pub async fn lookup(
resolver: &TokioAsyncResolver,
name: &String,
name: &Name,
do_full_lookup: bool,
) -> DnsLookupResult {
let (
@ -219,10 +228,10 @@ pub async fn lookup(
cname_lookup_res,
aname_lookup_res
) = join!(
resolver.lookup(name, RecordType::A),
resolver.lookup(name, RecordType::AAAA),
resolver.lookup(name, RecordType::CNAME),
resolver.lookup(name, RecordType::ANAME),
resolver.lookup(name.clone(), RecordType::A),
resolver.lookup(name.clone(), RecordType::AAAA),
resolver.lookup(name.clone(), RecordType::CNAME),
resolver.lookup(name.clone(), RecordType::ANAME),
);
// initlize an empty lookup result
@ -243,12 +252,12 @@ pub async fn lookup(
srv_lookup_res,
txt_lookup_res
) = join!(
resolver.lookup(name, RecordType::MX),
resolver.lookup(name, RecordType::NS),
resolver.lookup(name, RecordType::SOA),
resolver.lookup(name, RecordType::CAA),
resolver.lookup(name, RecordType::SRV),
resolver.lookup(name, RecordType::TXT),
resolver.lookup(name.clone(), RecordType::MX),
resolver.lookup(name.clone(), RecordType::NS),
resolver.lookup(name.clone(), RecordType::SOA),
resolver.lookup(name.clone(), RecordType::CAA),
resolver.lookup(name.clone(), RecordType::SRV),
resolver.lookup(name.clone(), RecordType::TXT),
);
integrate_lookup_result(&mut dig_result, mx_lookup_res);

View File

@ -120,7 +120,7 @@ impl Engine {
View::NotFound => *response.status_mut() = StatusCode::NOT_FOUND,
_ => {},
}
let cookie = Cookie::build("dns_resolver",settings.dns_resolver_id.clone())
let cookie = Cookie::build("dns_resolver",settings.dns_resolver_id.to_string())
.path("/")
.same_site(cookie::SameSite::Strict)
.finish();

View File

@ -26,8 +26,16 @@
<section>
<h2>DNS Records</h2>
{% if r.nxdomain %}
<p class="error box">Our DNS-Server claims that this domain doesn't exist, there shouldn't be any results.</p>
{% set show_nonpresent = true %}
{% if r.unkown_resolver %}
<p class="error box">The resolver you chose is not one of the available ones, if you can reproduce this error by just using the UI <a href="https://codeberg.org/slatian/service.echoip-slatecave/issues/new">please report it</a>.</p>
{% set show_nonpresent = false %}
{% elif r.invalid_name %}
<p class="error box">This domain name does not conform to <a href="https://www.rfc-editor.org/info/std3">the dns specification (std3)</a> rules and was therefore not resolved.</p>
{% set show_nonpresent = false %}
{% elif r.nxdomain %}
<p class="error box">The DNS-Server claims that this domain doesn't exist, there shouldn't be any results.</p>
{% set show_nonpresent = false %}
{% elif r.timeout %}
<p class="error box">There was at least one timeout error while resolving this domain, the results below are incomplete.</p>
{% elif r.other_error %}
@ -66,7 +74,7 @@
<li>{{ helper::ip(extra=extra, ip=address) }}</li>
{% endfor %}
</ul>
{% elif not r.nxdomain %}
{% elif show_nonpresent %}
<p>No <code>A</code> (IPv4) Records.</p>
{% endif %}
@ -77,7 +85,7 @@
<li>{{ helper::ip(extra=extra, ip=address) }}</li>
{% endfor %}
</ul>
{% elif not r.nxdomain %}
{% elif show_nonpresent %}
<p>No <code>AAAA</code> (IPv6) Records.</p>
{% endif %}
@ -90,7 +98,7 @@
<li>{{ helper::dig(extra=extra, name=mx.exchange, fqdn=true, prefix=mx.preference) }}</li>
{% endfor %}
</ul>
{% elif not r.nxdomain %}
{% elif show_nonpresent %}
<p id="mx">No <code>MX</code> (Mail Exchange) records.</p>
{% endif %}
@ -116,7 +124,7 @@
</dl></li>
{% endfor %}
</ul>
{% elif not r.nxdomain %}
{% elif show_nonpresent %}
<p id="soa">No <code>SOA</code> records.</p>
{% endif %}
@ -129,7 +137,7 @@
<li>{{ helper::dig(extra=extra, name=ns) }}</li>
{% endfor %}
</ul>
{% elif not r.nxdomain %}
{% elif show_nonpresent %}
<p id="ns">No <code>NS</code> (Name Server) records.</p>
{% endif %}
@ -141,7 +149,7 @@
<li><code>{{caa}}</code></li>
{% endfor %}
</ul>
{% elif not r.nxdomain %}
{% elif show_nonpresent %}
<p id="caa">No <code>CAA</code> (<a target="_blank" href="https://de.wikipedia.org/wiki/DNS_Certification_Authority_Authorization">Certification Authority Authorization</a>) records.</p>
{% endif %}
@ -152,7 +160,7 @@
<li><code>{{txt}}</code></li>
{% endfor %}
</ul>
{% elif not r.nxdomain %}
{% elif show_nonpresent %}
<p id="txt">No <code>TXT</code> records.</p>
{% endif %}
@ -172,7 +180,7 @@
</dl></li>
{% endfor %}
</ul>
{% elif not r.nxdomain %}
{% elif show_nonpresent %}
<p id="srv">No <code>SRV</code> records.</p>
<p><code>SRV</code> or Service records usually live on their own subdomains like {{ helper::dig(extra=extra, name="_xmpp-client._tcp."~data.query) }}.
{% endif %}

View File

@ -2,7 +2,8 @@
{% block path %}dig/{{ data.query | urlencode_strict }}{% endblock %}
{% block content -%}
{% set r = data.result.records %}
{%- block content -%}
# dig {{data.query}} via {{ data.result.used_dns_resolver }}
{% if data.result.idn -%}
@ -25,7 +26,16 @@ Your IDN would decode to
{% set r = data.result.records -%}
## DNS Records
{% if r.nxdomain %}
{% if r.unkown_resolver %}
{%- set show_nonpresent = false %}
The resolver you chose is not one of the available ones.
=> {{ extra.base_url }}/dns_resolver
{% elif r.invalid_name %}
{%- set show_nonpresent = false %}
This domain name does not conform to the dns specification (std3) rules and was therefore not resolved.
=> https://www.rfc-editor.org/info/std3
{% elif r.nxdomain %}
{%- set show_nonpresent = false %}
Our DNS-Server claims that this domain doesn't exist, there shouldn't be any results.
{%- elif r.timeout -%}
There was at least one timeout error while resolving this domain, the results below are incomplete.
@ -61,7 +71,7 @@ A (IPv4) records:
{% for address in r.a -%}
* {{ address }}
{% endfor %}
{%- elif not r.nxdomain %}
{%- elif show_nonpresent %}
No A (IPv4) Records.
{% endif -%}
@ -70,7 +80,7 @@ AAAA (IPv6) records:
{% for address in r.aaaa -%}
* {{ address }}
{% endfor %}
{%- elif not r.nxdomain %}
{%- elif show_nonpresent %}
No AAAA (IPv6) Records.
{% endif -%}
@ -81,7 +91,7 @@ MX (Mail Exchange) records:
{% for mx in r.mx | sort(attribute="preference") | reverse -%}
* {{ mx.preference }} {{ mx.exchange }}
{% endfor %}
{%- elif not r.nxdomain %}
{%- elif show_nonpresent %}
No MX (Mail Exchange) records.
{% endif %}
@ -96,7 +106,7 @@ SOA (Source Of Authority) records:
* expire: {{soa.expire / 3600 | round(precision=2)}}h
* minimum: {{soa.minimum / 60 | round(precision=2)}}m TTL
{% endfor %}
{%- elif not r.nxdomain %}
{%- elif show_nonpresent %}
No SOA (Source Of Authority) records.
{% endif %}
@ -105,7 +115,7 @@ NS (Name Server) records:
{% for ns in r.ns -%}
* {{ns}}
{% endfor %}
{%- elif not r.nxdomain %}
{%- elif show_nonpresent %}
No NS (Name Server) records.
{% endif %}
@ -114,7 +124,7 @@ CAA (Certification Authority Authorization) records:
{% for caa in r.caa -%}
* {{caa}}
{% endfor %}
{%- elif not r.nxdomain %}
{%- elif show_nonpresent %}
No CAA (Certification Authority Authorization) records.
{% endif %}
@ -123,7 +133,7 @@ TXT records:
{% for txt in r.txt -%}
* {{txt}}
{% endfor %}
{%- elif not r.nxdomain %}
{%- elif show_nonpresent %}
No TXT records.
{% endif %}
@ -135,7 +145,7 @@ SRV records:
* Port: {{srv.port}}
* Target: {{srv.target}}
{% endfor %}
{%- elif not r.nxdomain %}
{%- elif show_nonpresent %}
No SRV records.
SRV or Service records usually live on their own subdomains like {{ "_xmpp-client._tcp."~data.query }}.

View File

@ -31,12 +31,6 @@
<dd>{{ helper::dig(extra=extra, name=c.tls_dns_name) }}</dd>
{%- endif %}
{%- if c.search | length > 0 %}
<dt>Search</dt>
{%- for s in c.search %}
<dd>{{s}}</dd>
{%- endfor %}
{%- endif %}
</dl>
{%- if c.info_url %}
<p class="button-paragraph"><a href="{{c.info_url}}">More about the {{c.display_name}} DNS Server <small>(external link)</small></a></p>

View File

@ -16,14 +16,6 @@ Protocol: {{ c.protocol }}
{%-if c.tls_dns_name %}
DNS Name: {{ c.tls_dns_name }}
{%- endif %}
{%- if c.search | length == 1 %}
Search: {{ c.search | first }}
{%- elif c.search | length > 1 %}
Search:
{%- for s in c.search %}
* {{s}}
{%- endfor %}
{%- endif %}
{%- if c.aliases | length == 1 %}
Alias: {{ c.aliases | first }}
{%- elif c.aliases | length > 1 %}

View File

@ -380,6 +380,8 @@ a:visited {
color: var(--page-link-visited);
}
a.sitename { display: inline-block; }
h1, a.sitename {
margin: var(--heading-mg);
padding: var(--heading-pad);