Skip to content

dns

dns

NIP-66 DNS metadata container with resolution capabilities.

Performs comprehensive DNS resolution for a relay hostname, including A, AAAA, CNAME, NS, and PTR record lookups as part of NIP-66 monitoring. Clearnet relays only.

Note

Unlike the simpler resolve_host utility (which uses the system resolver for A/AAAA only), this module uses the dnspython library for comprehensive record collection. Individual record type lookups are wrapped in exception suppression so that a failure in one type does not prevent the others from being collected.

NS records are resolved against the registered domain (e.g., damus.io for relay.damus.io) using tldextract to identify the public suffix boundary. Reverse DNS (PTR) uses the first resolved IPv4 address.

See Also

bigbrotr.nips.nip66.data.Nip66DnsData: Data model for DNS resolution results. bigbrotr.nips.nip66.logs.Nip66DnsLogs: Log model for DNS resolution results. bigbrotr.utils.dns.resolve_host: Simpler A/AAAA-only resolution used by geo and net tests. bigbrotr.nips.nip66.geo.Nip66GeoMetadata: Geolocation test that depends on IP resolution. bigbrotr.nips.nip66.net.Nip66NetMetadata: Network/ASN test that depends on IP resolution.

Classes

Nip66DnsMetadata

Bases: BaseNipMetadata

Container for DNS resolution data and operation logs.

Provides the execute() class method that performs a comprehensive set of DNS queries (A, AAAA, CNAME, NS, reverse PTR) for a relay hostname.

See Also

bigbrotr.nips.nip66.nip66.Nip66: Top-level model that orchestrates this alongside other tests. bigbrotr.models.metadata.MetadataType: The NIP66_DNS variant used when storing these results.

Functions
execute async classmethod
execute(relay: Relay, timeout: float | None = None) -> Self

Resolve DNS records for a clearnet relay.

Runs the synchronous DNS resolver in a thread pool to avoid blocking the event loop.

Parameters:

  • relay (Relay) –

    Clearnet relay to resolve.

  • timeout (float | None, default: None ) –

    Resolver timeout in seconds (default: 10.0).

Returns:

  • Self

    An Nip66DnsMetadata instance with resolution data and logs.

Source code in src/bigbrotr/nips/nip66/dns.py
@classmethod
async def execute(
    cls,
    relay: Relay,
    timeout: float | None = None,  # noqa: ASYNC109
) -> Self:
    """Resolve DNS records for a clearnet relay.

    Runs the synchronous DNS resolver in a thread pool to avoid
    blocking the event loop.

    Args:
        relay: Clearnet relay to resolve.
        timeout: Resolver timeout in seconds (default: 10.0).

    Returns:
        An ``Nip66DnsMetadata`` instance with resolution data and logs.
    """
    timeout = timeout if timeout is not None else DEFAULT_TIMEOUT
    logger.debug("dns_testing relay=%s timeout_s=%s", relay.url, timeout)

    if relay.network != NetworkType.CLEARNET:
        return cls(
            data=Nip66DnsData(),
            logs=Nip66DnsLogs(
                success=False, reason=f"requires clearnet, got {relay.network.value}"
            ),
        )

    logs: dict[str, Any] = {"success": False, "reason": None}
    data: dict[str, Any] = {}

    try:
        logger.debug("dns_resolving host=%s", relay.host)
        data = await asyncio.to_thread(cls._dns, relay.host, timeout)
        if data:
            logs["success"] = True
            logger.debug("dns_completed relay=%s ips=%s", relay.url, data.get("dns_ips"))
        else:
            logs["reason"] = "no DNS records found"
            logger.debug("dns_no_data relay=%s", relay.url)
    except (OSError, dns.exception.DNSException) as e:
        logs["reason"] = str(e) or type(e).__name__
        logger.debug("dns_error relay=%s error=%s", relay.url, str(e))

    return cls(
        data=Nip66DnsData.model_validate(Nip66DnsData.parse(data)),
        logs=Nip66DnsLogs.model_validate(logs),
    )