# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
#
# SPDX-License-Identifier: MPL-2.0
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0.  If a copy of the MPL was not distributed with this
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.

from datetime import timedelta
import time

from dns import rdatatype

import pytest

import isctest

# isctest.asyncserver requires dnspython >= 2.0.0
pytest.importorskip("dns", minversion="2.0.0")


pytestmark = pytest.mark.extra_artifacts(
    [
        "*/K*",
        "*/dsset-*",
        "*/*.bk",
        "*/*.conf",
        "*/*.db",
        "*/*.id",
        "*/*.jnl",
        "*/*.jbk",
        "*/*.key",
        "*/*.signed",
        "*/settime.out.*",
        "ans*/ans.run",
        "*/trusted.keys",
        "*/*.bad",
        "*/*.next",
        "*/*.stripped",
        "*/*.tmp",
        "*/*.stage?",
        "*/*.patched",
        "*/*.lower",
        "*/*.upper",
        "*/*.unsplit",
    ]
)


def is_rrsig_soa(rrset):
    return rrset.rdtype == rdatatype.RRSIG and rrset.covers == rdatatype.SOA


def test_signatures_validity(ns3, templates):
    # check that increasing signatures-validity triggers resigning
    msg = isctest.query.create("siginterval.example.", "AXFR")
    res = isctest.query.tcp(msg, "10.53.0.3")
    before = next(filter(is_rrsig_soa, res.answer))

    templates.render("ns3/named.conf", {"long_sigs": True})
    with ns3.watch_log_from_here() as watcher:
        ns3.reconfigure()
        watcher.wait_for_line(
            "zone_needdump: zone siginterval.example/IN (signed): enter"
        )

    res = isctest.query.tcp(msg, "10.53.0.3")
    after = next(filter(is_rrsig_soa, res.answer))

    assert after != before

    ns3.rndc("sign siginterval.example")

    msg = isctest.query.create("siginterval.example.", "SOA")
    res = isctest.query.tcp(msg, "10.53.0.3")
    sexp = res.answer[-1][0].expiration

    msg = isctest.query.create("siginterval.example.", "DNSKEY")
    res = isctest.query.tcp(msg, "10.53.0.3")
    kexp = res.answer[-1][0].expiration

    delta = timedelta(seconds=kexp - sexp)
    assert delta > timedelta(days=54)


def test_signatures_validity_hours_vs_days():
    # zone configured with 'signatures-validity 500d; signatures-refresh 1d'
    msg = isctest.query.create("hours-vs-days.", "AXFR")
    res = isctest.query.tcp(msg, "10.53.0.2")

    # 499 days in the future w/ a 20 minute runtime to now allowance
    future = timedelta(days=499) - timedelta(minutes=20)
    minimum = time.time() + future.total_seconds()
    for rrset in res.answer:
        if rrset.rdtype != rdatatype.RRSIG:
            continue
        assert rrset[0].expiration >= minimum


def test_nsec_chain():
    # check that NSEC records are properly generated when DNSKEYs
    # are added by dnssec-policy
    msg = isctest.query.create("auto-nsec.example", "A")
    res = isctest.query.tcp(msg, "10.53.0.4")
    isctest.check.noerror(res)
    isctest.check.adflag(res)
    assert [a for a in res.authority if a.rdtype == rdatatype.NSEC]


def test_nsec3_chain():
    # check that NSEC3 records are properly generated when DNSKEYs
    # are added by dnssec-policy
    msg = isctest.query.create("auto-nsec3.example", "A")
    res = isctest.query.tcp(msg, "10.53.0.4")
    isctest.check.noerror(res)
    isctest.check.adflag(res)
    assert [a for a in res.authority if a.rdtype == rdatatype.NSEC3]
