All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.measite.minidns.iterative.ReliableDNSClient Maven / Gradle / Ivy

/*
 * Copyright 2015-2016 the original author or authors
 *
 * This software is licensed under the Apache License, Version 2.0,
 * the GNU Lesser General Public License version 2 or later ("LGPL")
 * and the WTFPL.
 * You may choose either license to govern your use of this software only
 * upon the condition that you accept all of the terms of either
 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
 */
package de.measite.minidns.iterative;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;

import de.measite.minidns.AbstractDNSClient;
import de.measite.minidns.DNSCache;
import de.measite.minidns.DNSClient;
import de.measite.minidns.DNSMessage;
import de.measite.minidns.Question;
import de.measite.minidns.source.DNSDataSource;
import de.measite.minidns.util.MultipleIoException;

/**
 * A DNS client using a reliable strategy. First the configured resolver of the
 * system are used, then, in case there is no answer, a fall back to iterative
 * resolving is performed.
 */
public class ReliableDNSClient extends AbstractDNSClient {

    public enum Mode {
        /**
         * Try the recursive servers first and fallback to iterative resolving if it fails. This is the default mode.
         */
        recursiveWithIterativeFallback,

        /**
         * Only try the recursive servers. This makes {@code ReliableDNSClient} behave like a {@link DNSClient}.
         */
        recursiveOnly,

        /**
         * Only use iterative resolving.  This makes {@code ReliableDNSClient} behave like a {@link IterativeDNSClient}.
         */
        iterativeOnly,
    }

    private final IterativeDNSClient recursiveDnsClient;
    private final DNSClient dnsClient;

    private Mode mode = Mode.recursiveWithIterativeFallback;

    public ReliableDNSClient(DNSCache dnsCache) {
        super(dnsCache);
        recursiveDnsClient = new IterativeDNSClient(dnsCache) {
            @Override
            protected DNSMessage.Builder newQuestion(DNSMessage.Builder questionMessage) {
                questionMessage = super.newQuestion(questionMessage);
                return ReliableDNSClient.this.newQuestion(questionMessage);
            }
            @Override
            protected boolean isResponseCacheable(Question q, DNSMessage dnsMessage) {
                boolean res = super.isResponseCacheable(q, dnsMessage);
                return ReliableDNSClient.this.isResponseCacheable(q, dnsMessage) && res;
            }
        };
        dnsClient = new DNSClient(dnsCache) {
            @Override
            protected DNSMessage.Builder newQuestion(DNSMessage.Builder questionMessage) {
                questionMessage = super.newQuestion(questionMessage);
                return ReliableDNSClient.this.newQuestion(questionMessage);
            }
            @Override
            protected boolean isResponseCacheable(Question q, DNSMessage dnsMessage) {
                boolean res = super.isResponseCacheable(q, dnsMessage);
                return ReliableDNSClient.this.isResponseCacheable(q, dnsMessage) && res;
            }
        };
    }

    public ReliableDNSClient() {
        this(DEFAULT_CACHE);
    }

    @Override
    protected DNSMessage query(DNSMessage.Builder q) throws IOException {
        DNSMessage dnsMessage = null;
        String unacceptableReason = null;
        List ioExceptions = new LinkedList<>();

        if (mode != Mode.iterativeOnly) {
            // Try a recursive query.
            try {
                dnsMessage = dnsClient.query(q);
                if (dnsMessage != null) {
                    unacceptableReason = isResponseAcceptable(dnsMessage);
                    if (unacceptableReason == null) {
                        return dnsMessage;
                    }
                }
            } catch (IOException ioException) {
                ioExceptions.add(ioException);
            }
        }

        // Abort if we the are in "recursive only" mode.
        if (mode == Mode.recursiveOnly) return dnsMessage;

        // Eventually log that we fall back to iterative mode.
        final Level FALLBACK_LOG_LEVEL = Level.FINE;
        if (LOGGER.isLoggable(FALLBACK_LOG_LEVEL) && mode != Mode.iterativeOnly) {
            String logString = "Resolution fall back to iterative mode because: ";
            if (!ioExceptions.isEmpty()) {
                logString += ioExceptions.get(0);
            } else if (dnsMessage == null) {
                logString += " DNSClient did not return a response";
            } else if (unacceptableReason != null) {
                logString += unacceptableReason + ". Response:\n" + dnsMessage;
            } else {
                throw new AssertionError("This should never been reached");
            }
            LOGGER.log(FALLBACK_LOG_LEVEL, logString);
        }

        try {
            dnsMessage = recursiveDnsClient.query(q);
        } catch (IOException ioException) {
            ioExceptions.add(ioException);
        }

        if (dnsMessage == null) {
            MultipleIoException.throwIfRequired(ioExceptions);
        }

        return dnsMessage;
    }

    @Override
    protected DNSMessage.Builder newQuestion(DNSMessage.Builder questionMessage) {
        return questionMessage;
    }

    @Override
    protected boolean isResponseCacheable(Question q, DNSMessage dnsMessage) {
        return isResponseAcceptable(dnsMessage) == null;
    }

    /**
     * Check if the response from the system's nameserver is acceptable. Must return null if the response
     * is acceptable, or a String describing why it is not acceptable. If the response is not acceptable then
     * {@link ReliableDNSClient} will fall back to resolve the query iteratively.
     *
     * @param response the response we got from the system's nameserver.
     * @return null if the response is acceptable, or a String if not.
     */
    protected String isResponseAcceptable(DNSMessage response) {
        return null;
    }

    @Override
    public void setDataSource(DNSDataSource dataSource) {
        super.setDataSource(dataSource);
        recursiveDnsClient.setDataSource(dataSource);
        dnsClient.setDataSource(dataSource);
    }

    /**
     * Set the mode used when resolving queries.
     *
     * @param mode the mode to use.
     */
    public void setMode(Mode mode) {
        if (mode == null) {
            throw new IllegalArgumentException("Mode must not be null.");
        }
        this.mode = mode;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy