mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Merge pull request #730 from dan-da/hiddenservices_pr
DNS lookups over Tor and connecting bitcoinj to .onion addresses.
This commit is contained in:
commit
a1e7ccc1bf
@ -1,175 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Micheal Swiggs
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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 io.bitsquare.btc;
|
||||
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
import com.runjva.sourceforge.jsocks.protocol.SocksSocket;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.net.discovery.PeerDiscovery;
|
||||
import org.bitcoinj.net.discovery.PeerDiscoveryException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/**
|
||||
* SeedPeersSocks5Dns resolves peers via Proxy (Socks5) remote DNS.
|
||||
*/
|
||||
public class SeedPeersSocks5Dns implements PeerDiscovery {
|
||||
private Socks5Proxy proxy;
|
||||
private NetworkParameters params;
|
||||
private InetSocketAddress[] seedAddrs;
|
||||
private InetSocketAddress[] seedAddrsIP;
|
||||
private int pnseedIndex;
|
||||
|
||||
private final InetSocketAddress[] seedAddrsResolved;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SeedPeersSocks5Dns.class);
|
||||
|
||||
/**
|
||||
* Supports finding peers by hostname over a socks5 proxy.
|
||||
*
|
||||
* @param Socks5Proxy proxy the socks5 proxy to connect over.
|
||||
* @param NetworkParameters param to be used for seed and port information.
|
||||
*/
|
||||
public SeedPeersSocks5Dns(Socks5Proxy proxy, NetworkParameters params) {
|
||||
|
||||
this.proxy = proxy;
|
||||
this.params = params;
|
||||
this.seedAddrs = convertAddrsString(params.getDnsSeeds(), params.getPort());
|
||||
|
||||
if (false) {
|
||||
// This is an example of how .onion servers could be used. Unfortunately there is presently no way
|
||||
// to hand the onion address (or a connected socket) back to bitcoinj without it crashing in PeerAddress.
|
||||
// note: the onion addresses should be added into bitcoinj NetworkParameters classes, eg for mainnet, testnet
|
||||
// not here!
|
||||
this.seedAddrs = new InetSocketAddress[]{InetSocketAddress.createUnresolved("cajrifqkvalh2ooa.onion", 8333),
|
||||
InetSocketAddress.createUnresolved("bk7yp6epnmcllq72.onion", 8333)
|
||||
};
|
||||
}
|
||||
|
||||
seedAddrsResolved = new InetSocketAddress[seedAddrs.length];
|
||||
for (int idx = seedAddrs.length; idx < seedAddrsResolved.length; idx++) {
|
||||
seedAddrsResolved[idx] = seedAddrsIP[idx - seedAddrs.length];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts as an iterator, returning the address of each node in the list sequentially.
|
||||
* Once all the list has been iterated, null will be returned for each subsequent query.
|
||||
*
|
||||
* @return InetSocketAddress - The address/port of the next node.
|
||||
* @throws PeerDiscoveryException
|
||||
*/
|
||||
@Nullable
|
||||
public InetSocketAddress getPeer() throws PeerDiscoveryException {
|
||||
try {
|
||||
return nextPeer();
|
||||
} catch (UnknownHostException e) {
|
||||
throw new PeerDiscoveryException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* worker for getPeer()
|
||||
*/
|
||||
@Nullable
|
||||
private InetSocketAddress nextPeer() throws UnknownHostException, PeerDiscoveryException {
|
||||
if (seedAddrs == null || seedAddrs.length == 0) {
|
||||
throw new PeerDiscoveryException("No IP address seeds configured; unable to find any peers");
|
||||
}
|
||||
|
||||
if (pnseedIndex >= seedAddrsResolved.length) {
|
||||
return null;
|
||||
}
|
||||
if (seedAddrsResolved[pnseedIndex] == null) {
|
||||
seedAddrsResolved[pnseedIndex] = lookup(proxy, seedAddrs[pnseedIndex]);
|
||||
}
|
||||
log.error("SeedPeersSocks5Dns::nextPeer: " + seedAddrsResolved[pnseedIndex]);
|
||||
|
||||
return seedAddrsResolved[pnseedIndex++];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all the Bitcoin nodes within the list.
|
||||
*/
|
||||
@Override
|
||||
public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException {
|
||||
try {
|
||||
return allPeers();
|
||||
} catch (UnknownHostException e) {
|
||||
throw new PeerDiscoveryException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns all seed peers, performs hostname lookups if necessary.
|
||||
*/
|
||||
private InetSocketAddress[] allPeers() throws UnknownHostException {
|
||||
for (int i = 0; i < seedAddrsResolved.length; ++i) {
|
||||
if (seedAddrsResolved[i] == null) {
|
||||
seedAddrsResolved[i] = lookup(proxy, seedAddrs[i]);
|
||||
}
|
||||
}
|
||||
return seedAddrsResolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a hostname via remote DNS over socks5 proxy.
|
||||
*/
|
||||
@Nullable
|
||||
public static InetSocketAddress lookup(Socks5Proxy proxy, InetSocketAddress addr) {
|
||||
if (!addr.isUnresolved()) {
|
||||
return addr;
|
||||
}
|
||||
try {
|
||||
SocksSocket proxySocket = new SocksSocket(proxy, addr.getHostString(), addr.getPort());
|
||||
InetAddress addrResolved = proxySocket.getInetAddress();
|
||||
proxySocket.close();
|
||||
if (addrResolved != null) {
|
||||
log.debug("Resolved " + addr.getHostString() + " to " + addrResolved.getHostAddress());
|
||||
return new InetSocketAddress(addrResolved, addr.getPort());
|
||||
} else {
|
||||
// note: .onion nodes fall in here when proxy is Tor. But they have no IP address.
|
||||
// Unfortunately bitcoinj crashes in PeerAddress if it finds an unresolved address.
|
||||
log.error("Connected to " + addr.getHostString() + ". But did not resolve to address.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Error resolving " + addr.getHostString() + ". Exception:\n" + e.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of hostnames to array of unresolved InetSocketAddress
|
||||
*/
|
||||
private InetSocketAddress[] convertAddrsString(String[] addrs, int port) {
|
||||
InetSocketAddress[] list = new InetSocketAddress[addrs.length];
|
||||
for (int i = 0; i < addrs.length; i++) {
|
||||
list[i] = InetSocketAddress.createUnresolved(addrs[i], port);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
}
|
@ -32,7 +32,9 @@ import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ExceptionHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.network.DnsLookupTor;
|
||||
import io.bitsquare.network.Socks5ProxyProvider;
|
||||
import io.bitsquare.network.Socks5MultiDiscovery;
|
||||
import io.bitsquare.storage.FileUtil;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.user.Preferences;
|
||||
@ -266,9 +268,7 @@ public class WalletService {
|
||||
// Pass custom seed nodes if set in options
|
||||
if (!btcNodes.isEmpty()) {
|
||||
|
||||
// TODO: this parsing should be more robust,
|
||||
// give validation error if needed.
|
||||
String[] nodes = btcNodes.replace(", ", ",").split(",");
|
||||
String[] nodes = parseCSV(btcNodes);
|
||||
List<PeerAddress> peerAddressList = new ArrayList<>();
|
||||
for (String node : nodes) {
|
||||
String[] parts = node.split(":");
|
||||
@ -279,19 +279,24 @@ public class WalletService {
|
||||
if (parts.length == 2) {
|
||||
// note: this will cause a DNS request if hostname used.
|
||||
// note: DNS requests are routed over socks5 proxy, if used.
|
||||
// fixme: .onion hostnames will fail! see comments in SeedPeersSocks5Dns
|
||||
// note: .onion hostnames will be unresolved.
|
||||
InetSocketAddress addr;
|
||||
if (socks5Proxy != null) {
|
||||
InetSocketAddress unresolved = InetSocketAddress.createUnresolved(parts[0], Integer.parseInt(parts[1]));
|
||||
// proxy remote DNS request happens here.
|
||||
addr = SeedPeersSocks5Dns.lookup(socks5Proxy, unresolved);
|
||||
try {
|
||||
// proxy remote DNS request happens here. blocking.
|
||||
addr = new InetSocketAddress(DnsLookupTor.lookup(socks5Proxy, parts[0]), Integer.parseInt(parts[1]));
|
||||
}
|
||||
catch(Exception e) {
|
||||
log.warn("Dns lookup failed for host: {}", parts[0]);
|
||||
addr = null;
|
||||
}
|
||||
} else {
|
||||
// DNS request happens here. if it fails, addr.isUnresolved() == true.
|
||||
addr = new InetSocketAddress(parts[0], Integer.parseInt(parts[1]));
|
||||
}
|
||||
// note: isUnresolved check should be removed once we fix PeerAddress
|
||||
if (addr != null && !addr.isUnresolved())
|
||||
if (addr != null && !addr.isUnresolved()) {
|
||||
peerAddressList.add(new PeerAddress(addr.getAddress(), addr.getPort()));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (peerAddressList.size() > 0) {
|
||||
@ -343,9 +348,10 @@ public class WalletService {
|
||||
// could become outdated, so it is important that the user be able to
|
||||
// disable it, but should be made aware of the reduced privacy.
|
||||
if (socks5Proxy != null && !usePeerNodes) {
|
||||
// SeedPeersSocks5Dns should replace SeedPeers once working reliably.
|
||||
// SeedPeers uses hard coded stable addresses (from MainNetParams). It should be updated from time to time.
|
||||
walletAppKit.setDiscovery(new SeedPeers(params));
|
||||
// TODO: the discovery mode should come from command-line args, and default to ALL if not present.
|
||||
int discoveryMode = Socks5MultiDiscovery.SOCKS5_DISCOVER_ALL;
|
||||
walletAppKit.setDiscovery(new Socks5MultiDiscovery(socks5Proxy, params, discoveryMode));
|
||||
}
|
||||
|
||||
walletAppKit.setDownloadListener(downloadListener)
|
||||
@ -1130,6 +1136,19 @@ public class WalletService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* parses a comma separated string into String array.
|
||||
*
|
||||
* all spaces are stripped from the string, so this
|
||||
* method is not suitable for CSV with values that contain spaces.
|
||||
*/
|
||||
private String[] parseCSV(String buf) {
|
||||
// todo: improve parsing to handle multiple spaces, etc.
|
||||
return buf.replace(" ", "").split(",");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Inner classes
|
||||
|
@ -206,7 +206,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||
peerList.stream().forEach(e -> {
|
||||
if (bitcoinPeersTextArea.getText().length() > 0)
|
||||
bitcoinPeersTextArea.appendText("\n");
|
||||
bitcoinPeersTextArea.appendText(e.getAddress().getSocketAddress().toString());
|
||||
bitcoinPeersTextArea.appendText(e.toString());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
143
network/src/main/java/io/bitsquare/network/DnsLookupTor.java
Normal file
143
network/src/main/java/io/bitsquare/network/DnsLookupTor.java
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.network;
|
||||
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Performs DNS lookup over Socks5 proxy that implements the RESOLVE extension.
|
||||
* At this time, Tor is only known Socks5 proxy that supports it.
|
||||
*
|
||||
* Adapted from https://github.com/btcsuite/btcd/blob/master/connmgr/tor.go
|
||||
*/
|
||||
public class DnsLookupTor {
|
||||
private static final Logger log = LoggerFactory.getLogger(DnsLookupTor.class);
|
||||
private static final Map<Byte, String> torStatusErrors = DnsLookupTor.createMap();
|
||||
|
||||
private static Map<Byte, String> createMap() {
|
||||
HashMap<Byte, String> map = new HashMap<Byte, String>();
|
||||
map.put(Byte.valueOf(b('\u0000')), "tor succeeded");
|
||||
map.put(Byte.valueOf(b('\u0001')), "tor general error");
|
||||
map.put(Byte.valueOf(b('\u0002')), "tor not allowed");
|
||||
map.put(Byte.valueOf(b('\u0003')), "tor network is unreachable");
|
||||
map.put(Byte.valueOf(b('\u0004')), "tor host is unreachable");
|
||||
map.put(Byte.valueOf(b('\u0005')), "tor connection refused");
|
||||
map.put(Byte.valueOf(b('\u0006')), "tor TTL expired");
|
||||
map.put(Byte.valueOf(b('\u0007')), "tor command not supported");
|
||||
map.put(Byte.valueOf(b('\u0008')), "tor address type not supported");
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs DNS lookup and returns a single InetAddress
|
||||
*/
|
||||
public static InetAddress lookup(Socks5Proxy proxy, String host) throws Exception {
|
||||
Logger log = LoggerFactory.getLogger(DnsLookupTor.class);
|
||||
try {
|
||||
log.debug("Resolving {} over tor.", (Object)host);
|
||||
return DnsLookupTor.doLookup(proxy, host);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn("Error resolving " + host + ". Exception:\n" + e.toString());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual lookup is performed here.
|
||||
*/
|
||||
private static InetAddress doLookup(Socks5Proxy proxy, String host) throws Exception {
|
||||
|
||||
// note: This is creating a new connection to our proxy, without any authentication.
|
||||
// This works fine when connecting to bitsquare's internal Tor proxy, but
|
||||
// would fail if user has configured an external proxy that requires auth.
|
||||
// It would be much better to use the already connected proxy socket, but when I
|
||||
// tried that I get weird errors and the lookup fails.
|
||||
//
|
||||
// So this is an area for future improvement.
|
||||
Socket proxySocket = new Socket(proxy.getInetAddress(), proxy.getPort());
|
||||
|
||||
proxySocket.getOutputStream().write(new byte[]{b('\u0005'), b('\u0001'), b('\u0000')});
|
||||
byte[] buf = new byte[2];
|
||||
proxySocket.getInputStream().read(buf);
|
||||
|
||||
if (buf[0] != b('\u0005')) {
|
||||
throw new Exception("Invalid Proxy Response");
|
||||
}
|
||||
if (buf[1] != b('\u0000')) {
|
||||
throw new Exception("Unrecognized Tor Auth Method");
|
||||
}
|
||||
|
||||
byte[] hostBytes = host.getBytes();
|
||||
buf = new byte[7 + hostBytes.length];
|
||||
buf[0] = b('\u0005');
|
||||
buf[1] = b('\u00f0');
|
||||
buf[2] = b('\u0000');
|
||||
buf[3] = b('\u0003');
|
||||
buf[4] = (byte)hostBytes.length;
|
||||
System.arraycopy(hostBytes, 0, buf, 5, hostBytes.length);
|
||||
buf[5 + hostBytes.length] = 0;
|
||||
|
||||
proxySocket.getOutputStream().write(buf);
|
||||
|
||||
buf = new byte[4];
|
||||
proxySocket.getInputStream().read(buf);
|
||||
|
||||
if (buf[0] != b('\u0005')) {
|
||||
throw new Exception("Invalid Tor Proxy Response");
|
||||
}
|
||||
|
||||
if (buf[1] != b('\u0000')) {
|
||||
if (!torStatusErrors.containsKey(Byte.valueOf(buf[1]))) {
|
||||
throw new Exception("Invalid Tor Proxy Response");
|
||||
}
|
||||
throw new Exception(torStatusErrors.get(Byte.valueOf(buf[1])));
|
||||
}
|
||||
|
||||
if (buf[3] != b('\u0001')) {
|
||||
throw new Exception(torStatusErrors.get(Byte.valueOf(b('\u0001'))));
|
||||
}
|
||||
|
||||
buf = new byte[4];
|
||||
int bytesRead = proxySocket.getInputStream().read(buf);
|
||||
|
||||
if (bytesRead != 4) {
|
||||
throw new Exception("Invalid Tor Address Response");
|
||||
}
|
||||
|
||||
InetAddress addr = InetAddress.getByAddress(buf);
|
||||
return addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* so we can have prettier code without a bunch of casts.
|
||||
*/
|
||||
private static byte b(char c) {
|
||||
return (byte)c;
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.network;
|
||||
|
||||
import com.runjva.sourceforge.jsocks.protocol.*;
|
||||
|
||||
import org.bitcoinj.net.discovery.*;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.utils.*;
|
||||
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Supports peer discovery through DNS over Socks5 proxy with RESOLVE DNS extension.</p>
|
||||
*
|
||||
* (As of this writing, only Tor is known to support the RESOLVE DNS extension.)
|
||||
*
|
||||
* <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 Socks5DnsDiscovery extends MultiplexingDiscovery {
|
||||
|
||||
private Socks5Proxy proxy;
|
||||
|
||||
/**
|
||||
* 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 Socks5DnsDiscovery(Socks5Proxy proxy, NetworkParameters netParams) {
|
||||
this(proxy, netParams.getDnsSeeds(), netParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports finding peers through DNS A records.
|
||||
*
|
||||
* @param dnsSeeds Host names to be examined for seed addresses.
|
||||
* @param params Network parameters to be used for port information.
|
||||
*/
|
||||
public Socks5DnsDiscovery(Socks5Proxy proxy, String[] dnsSeeds, NetworkParameters params) {
|
||||
super(params, buildDiscoveries(proxy, params, dnsSeeds));
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
private static List<PeerDiscovery> buildDiscoveries(Socks5Proxy proxy, NetworkParameters params, String[] seeds) {
|
||||
List<PeerDiscovery> discoveries = new ArrayList<PeerDiscovery>(seeds.length);
|
||||
for (String seed : seeds) {
|
||||
if(seed == "dnsseed.bluematt.me") {
|
||||
continue; // this dns is known to fail with tor-resolve because it returns too many addrs.
|
||||
// we avoid adding it in order to prevent ugly log messages.
|
||||
}
|
||||
discoveries.add(new Socks5DnsSeedDiscovery(proxy, params, seed));
|
||||
}
|
||||
return discoveries;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExecutorService createExecutor() {
|
||||
// Attempted workaround for reported bugs on Linux in which gethostbyname does not appear to be properly
|
||||
// thread safe and can cause segfaults on some libc versions.
|
||||
if (System.getProperty("os.name").toLowerCase().contains("linux")) {
|
||||
return Executors.newSingleThreadExecutor(new ContextPropagatingThreadFactory("DNS seed lookups"));
|
||||
}
|
||||
else {
|
||||
return Executors.newFixedThreadPool(seeds.size(), new DaemonThreadFactory("DNS seed lookups"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Implements discovery from a single DNS host over Socks5 proxy with RESOLVE DNS extension. */
|
||||
public static class Socks5DnsSeedDiscovery implements PeerDiscovery {
|
||||
private final String hostname;
|
||||
private final NetworkParameters params;
|
||||
private final Socks5Proxy proxy;
|
||||
|
||||
public Socks5DnsSeedDiscovery(Socks5Proxy proxy, NetworkParameters params, String hostname) {
|
||||
this.hostname = hostname;
|
||||
this.params = params;
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns peer addresses. The actual DNS lookup is performed here.
|
||||
*/
|
||||
@Override
|
||||
public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException {
|
||||
try {
|
||||
InetSocketAddress addr = new InetSocketAddress(DnsLookupTor.lookup(proxy, hostname), params.getPort());
|
||||
return new InetSocketAddress[] {addr};
|
||||
}
|
||||
catch(Exception e) {
|
||||
throw new PeerDiscoveryException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return hostname;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.network;
|
||||
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
import com.runjva.sourceforge.jsocks.protocol.SocksSocket;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.net.discovery.PeerDiscovery;
|
||||
import org.bitcoinj.net.discovery.PeerDiscoveryException;
|
||||
import org.bitcoinj.net.discovery.SeedPeers;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements various types of discovery over Socks5,
|
||||
* which can be enabled/disabled via constructor flag.
|
||||
*/
|
||||
public class Socks5MultiDiscovery implements PeerDiscovery {
|
||||
|
||||
public static final int SOCKS5_DISCOVER_ADDR = 0x0001;
|
||||
public static final int SOCKS5_DISCOVER_DNS = 0x0010;
|
||||
public static final int SOCKS5_DISCOVER_ONION = 0x0100;
|
||||
public static final int SOCKS5_DISCOVER_ALL = 0x1111;
|
||||
|
||||
private ArrayList<PeerDiscovery> discoveryList;
|
||||
|
||||
/**
|
||||
* Supports finding peers by hostname over a socks5 proxy.
|
||||
*
|
||||
* @param Socks5Proxy proxy the socks5 proxy to connect over.
|
||||
* @param NetworkParameters param to be used for seed and port information.
|
||||
* @param mode specify discovery mode, OR'd together. one or more of:
|
||||
* SOCKS5_DISCOVER_ADDR
|
||||
* SOCKS5_DISCOVER_DNS
|
||||
* SOCKS5_DISCOVER_ONION
|
||||
* SOCKS5_DISCOVER_ALL
|
||||
*/
|
||||
public Socks5MultiDiscovery(Socks5Proxy proxy, NetworkParameters params, int mode) {
|
||||
|
||||
discoveryList = new ArrayList<PeerDiscovery>();
|
||||
|
||||
if( (mode & SOCKS5_DISCOVER_ONION) != 0) {
|
||||
discoveryList.add(new Socks5SeedOnionDiscovery(proxy, params));
|
||||
}
|
||||
if( (mode & SOCKS5_DISCOVER_ADDR) != 0) {
|
||||
// note: SeedPeers does not perform any network operations, so does not use proxy.
|
||||
discoveryList.add(new SeedPeers(params));
|
||||
}
|
||||
if( (mode & SOCKS5_DISCOVER_DNS) != 0) {
|
||||
discoveryList.add(new Socks5DnsDiscovery(proxy, params));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all the Bitcoin nodes that have been discovered.
|
||||
*/
|
||||
@Override
|
||||
public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException {
|
||||
ArrayList<InetSocketAddress> list = new ArrayList<InetSocketAddress>();
|
||||
|
||||
for (PeerDiscovery discovery : discoveryList) {
|
||||
list.addAll(Arrays.asList(discovery.getPeers(timeoutValue, timeoutUnit)));
|
||||
}
|
||||
|
||||
return list.toArray(new InetSocketAddress[list.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.network;
|
||||
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
import com.runjva.sourceforge.jsocks.protocol.SocksSocket;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.net.discovery.PeerDiscovery;
|
||||
import org.bitcoinj.net.discovery.PeerDiscoveryException;
|
||||
import org.bitcoinj.params.MainNetParams;
|
||||
import org.bitcoinj.params.RegTestParams;
|
||||
import org.bitcoinj.params.TestNet3Params;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* Socks5SeedOnionDiscovery provides a list of known Bitcoin .onion seeds.
|
||||
* These are nodes running as hidden services on the Tor network.
|
||||
*/
|
||||
public class Socks5SeedOnionDiscovery implements PeerDiscovery {
|
||||
private Socks5Proxy proxy;
|
||||
private NetworkParameters params;
|
||||
private InetSocketAddress[] seedAddrs;
|
||||
|
||||
/**
|
||||
* Supports finding peers by hostname over a socks5 proxy.
|
||||
*
|
||||
* @param Socks5Proxy proxy the socks5 proxy to connect over.
|
||||
* @param NetworkParameters param to be used for seed and port information.
|
||||
*/
|
||||
public Socks5SeedOnionDiscovery(Socks5Proxy proxy, NetworkParameters params) {
|
||||
|
||||
this.proxy = proxy;
|
||||
this.params = params;
|
||||
|
||||
// We do this because NetworkParameters does not contain any .onion
|
||||
// seeds. Perhaps someday...
|
||||
if(params == MainNetParams.get()) {
|
||||
this.seedAddrs = convertAddrsString(mainNetSeeds(), params.getPort());
|
||||
}
|
||||
else if(params == TestNet3Params.get()) {
|
||||
this.seedAddrs = convertAddrsString(testNet3Seeds(), params.getPort());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns .onion nodes available on mainnet
|
||||
*/
|
||||
private String[] mainNetSeeds() {
|
||||
// this list copied from bitcoin-core on 2017-01-19
|
||||
// https://github.com/bitcoin/bitcoin/blob/57b34599b2deb179ff1bd97ffeab91ec9f904d85/contrib/seeds/nodes_main.txt
|
||||
|
||||
return new String[] {
|
||||
"3ffk7iumtx3cegbi.onion",
|
||||
"3nmbbakinewlgdln.onion",
|
||||
"4j77gihpokxu2kj4.onion",
|
||||
"546esc6botbjfbxb.onion",
|
||||
"5at7sq5nm76xijkd.onion",
|
||||
"77mx2jsxaoyesz2p.onion",
|
||||
"7g7j54btiaxhtsiy.onion",
|
||||
"a6obdgzn67l7exu3.onion",
|
||||
"ab64h7olpl7qpxci.onion",
|
||||
"am2a4rahltfuxz6l.onion",
|
||||
"azuxls4ihrr2mep7.onion",
|
||||
"bitcoin7bi4op7wb.onion",
|
||||
"bitcoinostk4e4re.onion",
|
||||
"bk7yp6epnmcllq72.onion",
|
||||
"bmutjfrj5btseddb.onion",
|
||||
"ceeji4qpfs3ms3zc.onion",
|
||||
"clexmzqio7yhdao4.onion",
|
||||
"gb5ypqt63du3wfhn.onion",
|
||||
"h2vlpudzphzqxutd.onion",
|
||||
"ncwk3lutemffcpc4.onion",
|
||||
"okdzjarwekbshnof.onion",
|
||||
"pjghcivzkoersesd.onion",
|
||||
"rw7ocjltix26mefn.onion",
|
||||
"uws7itep7o3yinxo.onion",
|
||||
"vk3qjdehyy4dwcxw.onion",
|
||||
"vqpye2k5rcqvj5mq.onion",
|
||||
"wpi7rpvhnndl52ee.onion"
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* returns .onion nodes available on testnet3
|
||||
*/
|
||||
private String[] testNet3Seeds() {
|
||||
// this list copied from bitcoin-core on 2017-01-19
|
||||
// https://github.com/bitcoin/bitcoin/blob/57b34599b2deb179ff1bd97ffeab91ec9f904d85/contrib/seeds/nodes_test.txt
|
||||
return new String[] {
|
||||
"thfsmmn2jbitcoin.onion",
|
||||
"it2pj4f7657g3rhi.onion",
|
||||
"nkf5e6b7pl4jfd4a.onion",
|
||||
"4zhkir2ofl7orfom.onion",
|
||||
"t6xj6wilh4ytvcs7.onion",
|
||||
"i6y6ivorwakd7nw3.onion",
|
||||
"ubqj4rsu3nqtxmtp.onion"
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all the Bitcoin nodes within the list.
|
||||
*/
|
||||
@Override
|
||||
public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException {
|
||||
return seedAddrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of hostnames to array of unresolved InetSocketAddress
|
||||
*/
|
||||
private InetSocketAddress[] convertAddrsString(String[] addrs, int port) {
|
||||
InetSocketAddress[] list = new InetSocketAddress[addrs.length];
|
||||
for (int i = 0; i < addrs.length; i++) {
|
||||
list[i] = InetSocketAddress.createUnresolved(addrs[i], port);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user