io.vertx.core.impl.resolver.DnsResolverProvider.java Source code

Java tutorial

Introduction

Here is the source code for io.vertx.core.impl.resolver.DnsResolverProvider.java

Source

/*
 * Copyright (c) 2011-2013 The original author or authors
 *  ------------------------------------------------------
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  and Apache License v2.0 which accompanies this distribution.
 *
 *      The Eclipse Public License is available at
 *      http://www.eclipse.org/legal/epl-v10.html
 *
 *      The Apache License v2.0 is available at
 *      http://www.opensource.org/licenses/apache2.0.php
 *
 *  You may elect to redistribute this code under either of these licenses.
 */
package io.vertx.core.impl.resolver;

import io.netty.channel.ChannelFactory;
import io.netty.channel.EventLoop;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.resolver.AddressResolverGroup;
import io.netty.resolver.HostsFileParser;
import io.netty.resolver.NameResolver;
import io.netty.resolver.dns.DnsAddressResolverGroup;
import io.netty.resolver.dns.DnsNameResolverBuilder;
import io.netty.resolver.dns.DnsServerAddressStream;
import io.netty.resolver.dns.DnsServerAddresses;
import io.netty.util.NetUtil;
import io.netty.util.concurrent.EventExecutor;
import io.vertx.core.Context;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.VertxException;
import io.vertx.core.dns.AddressResolverOptions;
import io.vertx.core.impl.AddressResolver;
import io.vertx.core.impl.VertxImpl;
import io.vertx.core.spi.resolver.ResolverProvider;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
 */
public class DnsResolverProvider implements ResolverProvider {

    private final Vertx vertx;
    private final List<ResolverRegistration> resolvers = Collections.synchronizedList(new ArrayList<>());
    private AddressResolverGroup<InetSocketAddress> resolverGroup;

    public DnsResolverProvider(VertxImpl vertx, AddressResolverOptions options) {
        List<String> dnsServers = options.getServers();
        List<InetSocketAddress> serverList = new ArrayList<>();
        if (dnsServers != null && dnsServers.size() > 0) {
            for (String dnsServer : dnsServers) {
                int sep = dnsServer.indexOf(':');
                String ipAddress;
                int port;
                if (sep != -1) {
                    ipAddress = dnsServer.substring(0, sep);
                    port = Integer.parseInt(dnsServer.substring(sep + 1));
                } else {
                    ipAddress = dnsServer;
                    port = 53;
                }
                try {
                    serverList.add(new InetSocketAddress(
                            InetAddress.getByAddress(NetUtil.createByteArrayFromIpAddressString(ipAddress)), port));
                } catch (UnknownHostException e) {
                    throw new VertxException(e);
                }
            }
        } else {
            DnsServerAddressStream stream = DnsServerAddresses.defaultAddresses().stream();
            Set<InetSocketAddress> all = new HashSet<>();
            while (true) {
                InetSocketAddress address = stream.next();
                if (all.contains(address)) {
                    break;
                }
                serverList.add(address);
                all.add(address);
            }
        }
        DnsServerAddresses nameServerAddresses = options.isRotateServers()
                ? DnsServerAddresses.rotational(serverList)
                : DnsServerAddresses.sequential(serverList);

        Map<String, InetAddress> entries;
        if (options.getHostsPath() != null) {
            File file = vertx.resolveFile(options.getHostsPath()).getAbsoluteFile();
            try {
                if (!file.exists() || !file.isFile()) {
                    throw new IOException();
                }
                entries = HostsFileParser.parse(file);
            } catch (IOException e) {
                throw new VertxException("Cannot read hosts file " + file.getAbsolutePath());
            }
        } else if (options.getHostsValue() != null) {
            try {
                entries = HostsFileParser.parse(new StringReader(options.getHostsValue().toString()));
            } catch (IOException e) {
                throw new VertxException("Cannot read hosts config ", e);
            }
        } else {
            entries = HostsFileParser.parseSilently();
        }

        this.vertx = vertx;
        this.resolverGroup = new AddressResolverGroup<InetSocketAddress>() {

            @Override
            protected io.netty.resolver.AddressResolver<InetSocketAddress> newResolver(EventExecutor executor)
                    throws Exception {

                DnsAddressResolverGroup group = new DnsAddressResolverGroup(NioDatagramChannel.class,
                        nameServerAddresses) {
                    @Override
                    protected NameResolver<InetAddress> newNameResolver(EventLoop eventLoop,
                            ChannelFactory<? extends DatagramChannel> channelFactory,
                            DnsServerAddresses nameServerAddresses) throws Exception {
                        DnsNameResolverBuilder builder = new DnsNameResolverBuilder((EventLoop) executor);
                        builder.hostsFileEntriesResolver(inetHost -> {
                            InetAddress addr = entries.get(inetHost);
                            if (addr == null) {
                                addr = entries.get(inetHost.toLowerCase(Locale.ENGLISH));
                            }
                            return addr;
                        });
                        builder.channelType(NioDatagramChannel.class);
                        builder.nameServerAddresses(nameServerAddresses);
                        builder.optResourceEnabled(options.isOptResourceEnabled());
                        builder.ttl(options.getCacheMinTimeToLive(), options.getCacheMaxTimeToLive());
                        builder.negativeTtl(options.getCacheNegativeTimeToLive());
                        builder.queryTimeoutMillis(options.getQueryTimeout());
                        builder.maxQueriesPerResolve(options.getMaxQueries());
                        builder.recursionDesired(options.getRdFlag());
                        if (options.getSearchDomains() != null) {
                            builder.searchDomains(options.getSearchDomains());
                            int ndots = options.getNdots();
                            if (ndots == -1) {
                                ndots = AddressResolver.DEFAULT_NDOTS_RESOLV_OPTION;
                            }
                            builder.ndots(ndots);
                        }
                        return builder.build();
                    }
                };

                io.netty.resolver.AddressResolver<InetSocketAddress> resolver = group.getResolver(executor);
                resolvers.add(new ResolverRegistration(resolver, (EventLoop) executor));

                return resolver;
            }
        };
    }

    private static class ResolverRegistration {
        private final io.netty.resolver.AddressResolver<InetSocketAddress> resolver;
        private final EventLoop executor;

        ResolverRegistration(io.netty.resolver.AddressResolver<InetSocketAddress> resolver, EventLoop executor) {
            this.resolver = resolver;
            this.executor = executor;
        }
    }

    @Override
    public AddressResolverGroup<InetSocketAddress> resolver(AddressResolverOptions options) {
        return resolverGroup;
    }

    @Override
    public void close(Handler<Void> doneHandler) {
        Context context = vertx.getOrCreateContext();
        ResolverRegistration[] registrations = this.resolvers
                .toArray(new ResolverRegistration[this.resolvers.size()]);
        if (registrations.length == 0) {
            context.runOnContext(doneHandler);
            return;
        }
        AtomicInteger count = new AtomicInteger(registrations.length);
        for (ResolverRegistration registration : registrations) {
            Runnable task = () -> {
                registration.resolver.close();
                if (count.decrementAndGet() == 0) {
                    context.runOnContext(doneHandler);
                }
            };
            if (registration.executor.inEventLoop()) {
                task.run();
            } else {
                registration.executor.execute(task);
            }
        }
    }
}