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

net.lightbody.bmp.proxy.dns.DnsJavaResolver Maven / Gradle / Ivy

package net.lightbody.bmp.proxy.dns;

import com.google.common.net.InetAddresses;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xbill.DNS.Record;
import org.xbill.DNS.*;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * An {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver} that uses dnsjava to perform DNS lookups. This implementation provides full
 * cache manipulation capabilities.
 *
 * @deprecated The dnsjava resolver has been deprecated in favor of the standard JVM resolver and will be removed in BMP >2.1.
 */
public class DnsJavaResolver extends AbstractHostNameRemapper implements AdvancedHostResolver {
	private static final Logger log = LoggerFactory.getLogger(DnsJavaResolver.class);

	/**
	 * DNS cache used for dnsjava lookups.
	 */
	private final Cache cache = new Cache();

	/**
	 * Maximum number of times to retry a DNS lookup due to a failure to connect to the DNS server.
	 */
	private static final int DNS_NETWORK_FAILURE_RETRY_COUNT = 5;

	@Override
	public void clearDNSCache() {
		cache.clearCache();
	}

	@Override
	public void setPositiveDNSCacheTimeout(int timeout, TimeUnit timeUnit) {
		cache.setMaxCache((int) TimeUnit.SECONDS.convert(timeout, timeUnit));
	}

	@Override
	public void setNegativeDNSCacheTimeout(int timeout, TimeUnit timeUnit) {
		cache.setMaxNCache((int) TimeUnit.SECONDS.convert(timeout, timeUnit));
	}

	@Override
	public Collection resolveRemapped(String remappedHost) {
		// special case for IP literals: return the InetAddress without doing a dnsjava lookup. dnsjava seems to handle ipv4 literals
		// reasonably well, but does not handle ipv6 literals (with or without [] brackets) correctly.
		// note this does not work properly for ipv6 literals with a scope identifier, which is a known issue for InetAddresses.isInetAddress().
		// (dnsjava also handles the situation incorrectly)
		if (InetAddresses.isInetAddress(remappedHost)) {
			return Collections.singletonList(InetAddresses.forString(remappedHost));
		}

		// retrieve IPv4 addresses, then retrieve IPv6 addresses only if no IPv4 addresses are found. the current implementation always uses the
		// first returned address, so there is no need to look for IPv6 addresses if an IPv4 address is found.
		Collection ipv4addresses = resolveHostByType(remappedHost, Type.A);

		if (!ipv4addresses.isEmpty()) {
			return ipv4addresses;
		} else {
			return resolveHostByType(remappedHost, Type.AAAA);
		}
	}

	/**
	 * Resolves the specified host using dnsjava, retrieving addresses of the specified type.
	 *
	 * @param host hostname to resolve
	 * @param type one of {@link org.xbill.DNS.Type}, typically {@link org.xbill.DNS.Type#A} (IPv4) or {@link org.xbill.DNS.Type#AAAA} (IPv6).
	 * @return resolved addresses, or an empty collection if no addresses could be resolved
	 */
	protected Collection resolveHostByType(String host, int type) {
		Lookup lookup;
		try {
			lookup = new Lookup(host, type, DClass.IN);
		} catch (TextParseException e) {
			return Collections.emptyList();
		}

		lookup.setCache(cache);

		// we set the retry count to -1 because we want the first execution not be counted as a retry.
		int retryCount = -1;
		Record[] records;

		// we iterate while the status is TRY_AGAIN and DNS_NETWORK_FAILURE_RETRY_COUNT is not exceeded
		do {
			records = lookup.run();
			retryCount++;
		} while (lookup.getResult() == Lookup.TRY_AGAIN && retryCount < DNS_NETWORK_FAILURE_RETRY_COUNT);

		if (records == null) {
			// no records found, so could not resolve host
			return Collections.emptyList();
		}

		// convert the records we found into IPv4/IPv6 InetAddress objects
		List addrList = new ArrayList(records.length);

		// the InetAddresses returned by dnsjava include the trailing dot, e.g. "www.google.com." -- use the passed-in (or remapped) host value instead
		for (Record record : records) {
			if (record instanceof ARecord) {
				ARecord ipv4Record = (ARecord) record;

				try {
					InetAddress resolvedAddress = InetAddress.getByAddress(host, ipv4Record.getAddress().getAddress());

					addrList.add(resolvedAddress);
				} catch (UnknownHostException e) {
					// this should never happen, unless there is a bug in dnsjava
					log.warn("dnsjava resolver returned an invalid InetAddress for host: " + host, e);
					continue;
				}
			} else if (record instanceof AAAARecord) {
				AAAARecord ipv6Record = (AAAARecord) record;

				try {
					InetAddress resolvedAddress = InetAddress.getByAddress(host, ipv6Record.getAddress().getAddress());

					addrList.add(resolvedAddress);
				} catch (UnknownHostException e) {
					// this should never happen, unless there is a bug in dnsjava
					log.warn("dnsjava resolver returned an invalid InetAddress for host: " + host, e);
					continue;
				}
			}
		}

		return addrList;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy