/**
 * Copyright 2011 John Sample
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.bitcoin.net.discovery;

import com.google.bitcoin.core.NetworkParameters;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.*;

/**
 * <p>Supports peer discovery through DNS.</p>
 *
 * <p>Failure to resolve individual host names will not cause an Exception to be thrown.
 * However, if all hosts passed fail to resolve a PeerDiscoveryException will be thrown during getPeers().
 * </p>
 *
 * <p>DNS seeds do not attempt to enumerate every peer on the network. {@link DnsDiscovery#getPeers(long, java.util.concurrent.TimeUnit)}
 * will return up to 30 random peers from the set of those returned within the timeout period. If you want more peers
 * to connect to, you need to discover them via other means (like addr broadcasts).</p>
 */
public class DnsDiscovery implements PeerDiscovery {
    private static final Logger log = LoggerFactory.getLogger(DnsDiscovery.class);

    private final String[] hostNames;
    private final NetworkParameters netParams;

    /**
     * Supports finding peers through DNS A records. Community run DNS entry points will be used.
     *
     * @param netParams Network parameters to be used for port information.
     */
    public DnsDiscovery(NetworkParameters netParams) {
        this(netParams.getDnsSeeds(), netParams);
    }

    /**
     * Supports finding peers through DNS A records.
     *
     * @param hostNames Host names to be examined for seed addresses.
     * @param netParams Network parameters to be used for port information.
     */
    public DnsDiscovery(String[] hostNames, NetworkParameters netParams) {
        this.hostNames = hostNames;
        this.netParams = netParams;
    }

    public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException {
        if (hostNames == null)
            throw new PeerDiscoveryException("Unable to find any peers via DNS");

        // Java doesn't have an async DNS API so we have to do all lookups in a thread pool, as sometimes seeds go
        // hard down and it takes ages to give up and move on.
        ExecutorService threadPool = Executors.newFixedThreadPool(hostNames.length);
        try {
            List<Callable<InetAddress[]>> tasks = Lists.newArrayList();
            for (final String seed : hostNames)
                tasks.add(new Callable<InetAddress[]>() {
                    public InetAddress[] call() throws Exception {
                        return InetAddress.getAllByName(seed);
                    }
                });
            final List<Future<InetAddress[]>> futures = threadPool.invokeAll(tasks, timeoutValue, timeoutUnit);
            ArrayList<InetSocketAddress> addrs = Lists.newArrayList();
            for (int i = 0; i < futures.size(); i++) {
                Future<InetAddress[]> future = futures.get(i);
                if (future.isCancelled()) {
                    log.warn("{} timed out", hostNames[i]);
                    continue;  // Timed out.
                }
                final InetAddress[] inetAddresses;
                try {
                    inetAddresses = future.get();
                } catch (ExecutionException e) {
                    log.error("Failed to look up DNS seeds from {}: {}", hostNames[i], e.getMessage());
                    continue;
                }
                for (InetAddress addr : inetAddresses) {
                    addrs.add(new InetSocketAddress(addr, netParams.getPort()));
                }
            }
            if (addrs.size() == 0)
                throw new PeerDiscoveryException("Unable to find any peers via DNS");
            Collections.shuffle(addrs);
            threadPool.shutdownNow();
            return addrs.toArray(new InetSocketAddress[addrs.size()]);
        } catch (InterruptedException e) {
            throw new PeerDiscoveryException(e);
        } finally {
            threadPool.shutdown();
        }
    }

    /** We don't have a way to abort a DNS lookup, so this does nothing */
    public void shutdown() {
    }
}

@@ -27,16 +27,53 @@
  Sample%0A
+ * Copyright 2014 Andreas Schildbach%0A
  *%0A * Li
@@ -1706,24 +1706,23 @@
 tring%5B%5D 
-hostName
+dnsSeed
 s;%0A    p
@@ -2155,24 +2155,23 @@
  @param 
-hostName
+dnsSeed
 s Host n
@@ -2327,24 +2327,23 @@
 tring%5B%5D 
-hostName
+dnsSeed
 s, Netwo
@@ -2385,28 +2385,26 @@
 his.
-hostNames = hostName
+dnsSeeds = dnsSeed
 s;%0A 
@@ -2570,24 +2570,23 @@
     if (
-hostName
+dnsSeed
 s == nul
@@ -2586,16 +2586,40 @@
  == null
+ %7C%7C dnsSeeds.length == 0
 )%0A      
@@ -2650,33 +2650,58 @@
 overyException(%22
-U
+No DNS seeds configured; u
 nable to find an
@@ -2703,32 +2703,24 @@
 nd any peers
- via DNS
 %22);%0A%0A       
@@ -2956,24 +2956,23 @@
 eadPool(
-hostName
+dnsSeed
 s.length
@@ -3101,18 +3101,19 @@
 d : 
-hostNames)
+dnsSeeds) %7B
 %0A   
@@ -3339,16 +3339,30 @@
     %7D);%0A
+            %7D%0A
         
@@ -3728,18 +3728,28 @@
 g.warn(%22
+DNS seed 
 %7B%7D
+:
  timed o
@@ -3753,24 +3753,23 @@
 d out%22, 
-hostName
+dnsSeed
 s%5Bi%5D);%0A 
@@ -3949,24 +3949,118 @@
 ture.get();%0A
+                    log.info(%22DNS seed %7B%7D: got %7B%7D peers%22, dnsSeeds%5Bi%5D, inetAddresses.length);%0A
             
@@ -4127,17 +4127,30 @@
 .error(%22
-F
+DNS seed %7B%7D: f
 ailed to
@@ -4161,41 +4161,22 @@
 k up
- DNS seeds from %7B%7D: %7B%7D%22, hostName
+: %7B%7D%22, dnsSeed
 s%5Bi%5D

Reformat log messages in DnsDiscovery and add one log message for the success case, to make more clear what's going on.