Skip to content

Commit

Permalink
Merge p2p repository at eb22118
Browse files Browse the repository at this point in the history
  • Loading branch information
ripcurlx committed Sep 5, 2018
1 parent 4c470d3 commit 27b6fa6
Show file tree
Hide file tree
Showing 127 changed files with 15,777 additions and 0 deletions.
33 changes: 33 additions & 0 deletions p2p/build.gradle
@@ -0,0 +1,33 @@
plugins {
id 'java'
id 'maven'
}

group = 'network.bisq'
version = '-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
jcenter()
maven { url 'https://jitpack.io' }
maven { url 'https://raw.githubusercontent.com/JesusMcCloud/tor-binary/master/release/' }
}

dependencies {
compile 'network.bisq:bisq-common:-SNAPSHOT'
compile('com.github.JesusMcCloud.netlayer:tor.native:0.4.2') {
exclude(module: 'slf4j-api')
}
compile('org.apache.httpcomponents:httpclient:4.5.3') {
exclude(module: 'commons-logging')
}
compile 'net.sf.jopt-simple:jopt-simple:5.0.3'
compile 'org.fxmisc.easybind:easybind:1.0.3'
compileOnly 'org.projectlombok:lombok:1.16.16'
annotationProcessor 'org.projectlombok:lombok:1.16.16'
testCompile 'junit:junit:4.12'
testCompile 'org.jmockit:jmockit:1.30'
testCompileOnly 'org.projectlombok:lombok:1.16.16'
testAnnotationProcessor 'org.projectlombok:lombok:1.16.16'
}
2 changes: 2 additions & 0 deletions p2p/gradle.properties
@@ -0,0 +1,2 @@
systemProp.org.gradle.internal.http.connectionTimeout=120000
systemProp.org.gradle.internal.http.socketTimeout=120000
28 changes: 28 additions & 0 deletions p2p/src/main/java/bisq/network/DnsLookupException.java
@@ -0,0 +1,28 @@
/*
* This file is part of Bisq.
*
* Bisq 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.
*
* Bisq 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 Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.network;

public class DnsLookupException extends Exception {
public DnsLookupException(String message) {
super(message);
}

public DnsLookupException(Exception e) {
super(e);
}
}
136 changes: 136 additions & 0 deletions p2p/src/main/java/bisq/network/DnsLookupTor.java
@@ -0,0 +1,136 @@
/*
* This file is part of Bisq.
*
* Bisq 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.
*
* Bisq 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 Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.network;

import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;

import java.net.InetAddress;
import java.net.Socket;

import java.io.IOException;

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.
* <p/>
* 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<>();
map.put(b('\u0000'), "tor succeeded");
map.put(b('\u0001'), "tor general error");
map.put(b('\u0002'), "tor not allowed");
map.put(b('\u0003'), "tor network is unreachable");
map.put(b('\u0004'), "tor host is unreachable");
map.put(b('\u0005'), "tor connection refused");
map.put(b('\u0006'), "tor TTL expired");
map.put(b('\u0007'), "tor command not supported");
map.put(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 DnsLookupException {
try {
// note: This is creating a new connection to our proxy, without any authentication.
// This works fine when connecting to bisq'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];
//noinspection ResultOfMethodCallIgnored
proxySocket.getInputStream().read(buf);

if (buf[0] != b('\u0005')) {
throw new DnsLookupException("Invalid Proxy Response");
}
if (buf[1] != b('\u0000')) {
throw new DnsLookupException("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];
//noinspection UnusedAssignment
int bytesRead = proxySocket.getInputStream().read(buf);

// TODO: Should not be a length check here as well?
/* if (bytesRead != 4)
throw new DnsLookupException("Invalid Tor Address Response");*/


if (buf[0] != b('\u0005'))
throw new DnsLookupException("Invalid Tor Proxy Response");

if (buf[1] != b('\u0000')) {
if (!torStatusErrors.containsKey(buf[1])) {
throw new DnsLookupException("Invalid Tor Proxy Response");
}
throw new DnsLookupException(torStatusErrors.get(buf[1]) + "(host=" + host + ")");
}

if (buf[3] != b('\u0001'))
throw new DnsLookupException(torStatusErrors.get(b('\u0001')) + "(host=" + host + ")");

buf = new byte[4];
bytesRead = proxySocket.getInputStream().read(buf);

if (bytesRead != 4)
throw new DnsLookupException("Invalid Tor Address Response");

return InetAddress.getByAddress(buf);
} catch (IOException | DnsLookupException e) {
log.warn("Error resolving " + host + ". Exception:\n" + e.toString());
throw new DnsLookupException(e);
}
}

/**
* so we can have prettier code without a bunch of casts.
*/
private static byte b(char c) {
return (byte) c;
}
}
32 changes: 32 additions & 0 deletions p2p/src/main/java/bisq/network/NetworkOptionKeys.java
@@ -0,0 +1,32 @@
/*
* This file is part of Bisq.
*
* Bisq 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.
*
* Bisq 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 Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.network;

public class NetworkOptionKeys {
public static final String TOR_DIR = "torDir";
public static final String USE_LOCALHOST_FOR_P2P = "useLocalhostForP2P";
public static final String MAX_CONNECTIONS = "maxConnections";
public static final String PORT_KEY = "nodePort";
public static final String NETWORK_ID = "networkId";
public static final String SEED_NODES_KEY = "seedNodes";
public static final String MY_ADDRESS = "myAddress";
public static final String BAN_LIST = "banList";
//SOCKS_5_PROXY_BTC_ADDRESS used in network module so dont move it to BtcOptionKeys
public static final String SOCKS_5_PROXY_BTC_ADDRESS = "socks5ProxyBtcAddress";
public static final String SOCKS_5_PROXY_HTTP_ADDRESS = "socks5ProxyHttpAddress";
}
135 changes: 135 additions & 0 deletions p2p/src/main/java/bisq/network/Socks5DnsDiscovery.java
@@ -0,0 +1,135 @@
/*
* This file is part of Bisq.
*
* Bisq 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.
*
* Bisq 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 Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.network;

import bisq.common.util.Utilities;

import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.net.discovery.DnsDiscovery;
import org.bitcoinj.net.discovery.MultiplexingDiscovery;
import org.bitcoinj.net.discovery.PeerDiscovery;
import org.bitcoinj.net.discovery.PeerDiscoveryException;
import org.bitcoinj.utils.ContextPropagatingThreadFactory;
import org.bitcoinj.utils.DaemonThreadFactory;

import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;

import java.net.InetSocketAddress;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

/**
* <p>Supports peer discovery through DNS over Socks5 proxy with RESOLVE DNS extension.</p>
* <p>
* (As of this writing, only Tor is known to support the RESOLVE DNS extension.)
* <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>
* <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>
*/
@Slf4j
public class Socks5DnsDiscovery extends MultiplexingDiscovery {

/**
* 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));
}

private static List<PeerDiscovery> buildDiscoveries(Socks5Proxy proxy, NetworkParameters params, String[] seeds) {
List<PeerDiscovery> discoveries = new ArrayList<>(seeds.length);
for (String seed : seeds)
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 (Utilities.isLinux())
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.
* With our DnsLookupTor (used to not leak at DNS lookup) version we only get one address instead a list of addresses like in DnsDiscovery.
* We get repeated the call until we have received enough addresses.
*/
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 services, long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException {
if (services != 0)
throw new PeerDiscoveryException("DNS seeds cannot filter by services: " + services);
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 comments on commit 27b6fa6

Please sign in to comment.