001/*
002 * Copyright 2015-2024 the original author or authors
003 *
004 * This software is licensed under the Apache License, Version 2.0,
005 * the GNU Lesser General Public License version 2 or later ("LGPL")
006 * and the WTFPL.
007 * You may choose either license to govern your use of this software only
008 * upon the condition that you accept all of the terms of either
009 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
010 */
011package org.minidns.hla;
012
013import java.io.IOException;
014import java.util.Set;
015
016import org.minidns.DnsCache;
017import org.minidns.MiniDnsException.NullResultException;
018import org.minidns.cache.LruCache;
019import org.minidns.cache.MiniDnsCacheFactory;
020import org.minidns.dnsmessage.Question;
021import org.minidns.dnsname.DnsName;
022import org.minidns.dnssec.DnssecClient;
023import org.minidns.dnssec.DnssecQueryResult;
024import org.minidns.dnssec.DnssecUnverifiedReason;
025import org.minidns.iterative.ReliableDnsClient.Mode;
026import org.minidns.record.Data;
027import org.minidns.record.Record.TYPE;
028
029public class DnssecResolverApi extends ResolverApi {
030
031    public static final DnssecResolverApi INSTANCE = new DnssecResolverApi();
032
033    private final DnssecClient dnssecClient;
034    private final DnssecClient iterativeOnlyDnssecClient;
035    private final DnssecClient recursiveOnlyDnssecClient;
036
037    public DnssecResolverApi() {
038        this(new MiniDnsCacheFactory() {
039            @Override
040            public DnsCache newCache() {
041                return new LruCache();
042            }
043        });
044    }
045
046    public DnssecResolverApi(MiniDnsCacheFactory cacheFactory) {
047        this(new DnssecClient(cacheFactory.newCache()), cacheFactory);
048    }
049
050    private DnssecResolverApi(DnssecClient dnssecClient, MiniDnsCacheFactory cacheFactory) {
051        super(dnssecClient);
052        this.dnssecClient = dnssecClient;
053
054        // Set the *_ONLY_DNSSEC ResolverApi. It is important that the two do *not* share the same cache, since we
055        // probably fall back to iterativeOnly and in that case do not want the cached results of the recursive result.
056        iterativeOnlyDnssecClient = new DnssecClient(cacheFactory.newCache());
057        iterativeOnlyDnssecClient.setMode(Mode.iterativeOnly);
058
059        recursiveOnlyDnssecClient = new DnssecClient(cacheFactory.newCache());
060        recursiveOnlyDnssecClient.setMode(Mode.recursiveOnly);
061    }
062
063    @Override
064    public <D extends Data> ResolverResult<D> resolve(Question question) throws IOException {
065        DnssecQueryResult dnssecMessage = dnssecClient.queryDnssec(question);
066        return toResolverResult(question, dnssecMessage);
067    }
068
069    /**
070     * Resolve the given name and type which is expected to yield DNSSEC authenticated results.
071     *
072     * @param name the DNS name to resolve.
073     * @param type the class of the RR type to resolve.
074     * @param <D> the RR type to resolve.
075     * @return the resolver result.
076     * @throws IOException in case an exception happens while resolving.
077     * @see #resolveDnssecReliable(Question)
078     */
079    public <D extends Data> ResolverResult<D> resolveDnssecReliable(String name, Class<D> type) throws IOException {
080        return resolveDnssecReliable(DnsName.from(name), type);
081    }
082
083    /**
084     * Resolve the given name and type which is expected to yield DNSSEC authenticated results.
085     *
086     * @param name the DNS name to resolve.
087     * @param type the class of the RR type to resolve.
088     * @param <D> the RR type to resolve.
089     * @return the resolver result.
090     * @throws IOException in case an exception happens while resolving.
091     * @see #resolveDnssecReliable(Question)
092     */
093    public <D extends Data> ResolverResult<D> resolveDnssecReliable(DnsName name, Class<D> type) throws IOException {
094        TYPE t = TYPE.getType(type);
095        Question q = new Question(name, t);
096        return resolveDnssecReliable(q);
097    }
098
099    /**
100     * Resolve the given question which is expected to yield DNSSEC authenticated results.
101     *
102     * @param question the question to resolve.
103     * @param <D> the RR type to resolve.
104     * @return the resolver result.
105     * @throws IOException in case an exception happens while resolving.
106     */
107    public <D extends Data> ResolverResult<D> resolveDnssecReliable(Question question) throws IOException {
108        DnssecQueryResult dnssecMessage = recursiveOnlyDnssecClient.queryDnssec(question);
109        if (dnssecMessage == null || !dnssecMessage.isAuthenticData()) {
110            dnssecMessage = iterativeOnlyDnssecClient.queryDnssec(question);
111        }
112        return toResolverResult(question, dnssecMessage);
113    }
114
115    public DnssecClient getDnssecClient() {
116        return dnssecClient;
117    }
118
119    private static <D extends Data> ResolverResult<D> toResolverResult(Question question, DnssecQueryResult dnssecMessage) throws NullResultException {
120        Set<DnssecUnverifiedReason> unverifiedReasons = dnssecMessage.getUnverifiedReasons();
121
122        return new ResolverResult<D>(question, dnssecMessage.dnsQueryResult, unverifiedReasons);
123    }
124}