forked from FreicoinAlliance/freicoinj
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
260 changed files
with
32,486 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
270 changes: 270 additions & 0 deletions
270
core/src/main/java/com/google/bitcoin/net/discovery/TorDiscovery.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
/** | ||
* Copyright 2014 Miron Cuperman | ||
* | ||
* 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 com.google.common.collect.Maps; | ||
import com.google.common.collect.Sets; | ||
import com.google.common.util.concurrent.Futures; | ||
import com.google.common.util.concurrent.ListenableFuture; | ||
import com.google.common.util.concurrent.ListeningExecutorService; | ||
import com.google.common.util.concurrent.MoreExecutors; | ||
import com.subgraph.orchid.Circuit; | ||
import com.subgraph.orchid.RelayCell; | ||
import com.subgraph.orchid.Router; | ||
import com.subgraph.orchid.TorClient; | ||
import com.subgraph.orchid.circuits.path.CircuitPathChooser; | ||
import com.subgraph.orchid.data.HexDigest; | ||
import com.subgraph.orchid.data.exitpolicy.ExitTarget; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.net.InetAddress; | ||
import java.net.InetSocketAddress; | ||
import java.net.UnknownHostException; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.concurrent.Callable; | ||
import java.util.concurrent.ExecutionException; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
/** | ||
* <p>Supports peer discovery through Tor.</p> | ||
* | ||
* <p>Failure to obtain at least four different peers through different exit nodes will cause | ||
* a PeerDiscoveryException will be thrown during getPeers(). | ||
* </p> | ||
* | ||
* <p>DNS seeds do not attempt to enumerate every peer on the network. If you want more peers | ||
* to connect to, you need to discover them via other means (like addr broadcasts).</p> | ||
*/ | ||
public class TorDiscovery implements PeerDiscovery { | ||
private static final Logger log = LoggerFactory.getLogger(TorDiscovery.class); | ||
public static final int MINIMUM_ROUTER_COUNT = 4; | ||
public static final int MINIMUM_ROUTER_LOOKUP_COUNT = 10; | ||
public static final int RECEIVE_RETRIES = 3; | ||
public static final int RESOLVE_STREAM_ID = 0x1000; // An arbitrary stream ID | ||
public static final int RESOLVE_CNAME = 0x00; | ||
public static final int RESOLVE_ERROR = 0xf0; | ||
public static final int RESOLVE_IPV4 = 0x04; | ||
public static final int RESOLVE_IPV6 = 0x06; | ||
|
||
private final String[] hostNames; | ||
private final NetworkParameters netParams; | ||
private final CircuitPathChooser pathChooser; | ||
private final TorClient torClient; | ||
private ListeningExecutorService threadPool; | ||
|
||
/** | ||
* Supports finding peers through Tor. Community run DNS entry points will be used. | ||
* | ||
* @param netParams Network parameters to be used for port information. | ||
*/ | ||
public TorDiscovery(NetworkParameters netParams, TorClient torClient) { | ||
this(netParams.getDnsSeeds(), netParams, torClient); | ||
} | ||
|
||
/** | ||
* Supports finding peers through Tor. | ||
* | ||
* @param hostNames Host names to be examined for seed addresses. | ||
* @param netParams Network parameters to be used for port information. | ||
* @param torClient an already-started Tor client. | ||
*/ | ||
public TorDiscovery(String[] hostNames, NetworkParameters netParams, TorClient torClient) { | ||
this.hostNames = hostNames; | ||
this.netParams = netParams; | ||
|
||
this.torClient = torClient; | ||
this.pathChooser = CircuitPathChooser.create(torClient.getConfig(), torClient.getDirectory()); | ||
} | ||
|
||
private static class Lookup { | ||
final Router router; | ||
final InetAddress address; | ||
|
||
Lookup(Router router, InetAddress address) { | ||
this.router = router; | ||
this.address = address; | ||
} | ||
} | ||
|
||
public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException { | ||
if (hostNames == null) | ||
throw new PeerDiscoveryException("Unable to find any peers via DNS"); | ||
|
||
Set<Router> routers = Sets.newHashSet(); | ||
ArrayList<ExitTarget> dummyTargets = Lists.newArrayList(); | ||
|
||
// Collect exit nodes until we have enough | ||
while (routers.size() < MINIMUM_ROUTER_LOOKUP_COUNT) { | ||
Router router = pathChooser.chooseExitNodeForTargets(dummyTargets); | ||
routers.add(router); | ||
} | ||
|
||
try { | ||
List<Circuit> circuits = getCircuits(timeoutValue, timeoutUnit, routers); | ||
|
||
Collection<InetSocketAddress> addresses = lookupAddresses(timeoutValue, timeoutUnit, circuits); | ||
|
||
if (addresses.size() < MINIMUM_ROUTER_COUNT) | ||
throw new PeerDiscoveryException("Unable to find enough peers via Tor - got " + addresses.size()); | ||
ArrayList<InetSocketAddress> addressList = Lists.newArrayList(); | ||
addressList.addAll(addresses); | ||
Collections.shuffle(addressList); | ||
return addressList.toArray(new InetSocketAddress[addressList.size()]); | ||
} catch (InterruptedException e) { | ||
throw new PeerDiscoveryException(e); | ||
} | ||
} | ||
|
||
private List<Circuit> getCircuits(long timeoutValue, TimeUnit timeoutUnit, Set<Router> routers) throws InterruptedException { | ||
createThreadPool(routers.size()); | ||
|
||
try { | ||
List<ListenableFuture<Circuit>> circuitFutures = Lists.newArrayList(); | ||
for (final Router router : routers) { | ||
circuitFutures.add(threadPool.submit(new Callable<Circuit>() { | ||
public Circuit call() throws Exception { | ||
return torClient.getCircuitManager().openInternalCircuitTo(Lists.newArrayList(router)); | ||
} | ||
})); | ||
} | ||
|
||
threadPool.awaitTermination(timeoutValue, timeoutUnit); | ||
for (ListenableFuture<Circuit> future : circuitFutures) { | ||
if (!future.isDone()) { | ||
log.warn("circuit timed out"); | ||
future.cancel(true); | ||
} | ||
} | ||
|
||
List<Circuit> circuits; | ||
try { | ||
circuits = Futures.successfulAsList(circuitFutures).get(); | ||
// Any failures will result in null entries. Remove them. | ||
circuits.removeAll(Collections.singleton(null)); | ||
return circuits; | ||
} catch (ExecutionException e) { | ||
// Cannot happen, successfulAsList accepts failures | ||
throw new RuntimeException(e); | ||
} | ||
} finally { | ||
shutdownThreadPool(); | ||
} | ||
} | ||
|
||
private Collection<InetSocketAddress> lookupAddresses(long timeoutValue, TimeUnit timeoutUnit, List<Circuit> circuits) throws InterruptedException { | ||
createThreadPool(circuits.size() * hostNames.length); | ||
|
||
try { | ||
List<ListenableFuture<Lookup>> lookupFutures = Lists.newArrayList(); | ||
for (final Circuit circuit : circuits) { | ||
for (final String seed : hostNames) { | ||
lookupFutures.add(threadPool.submit(new Callable<Lookup>() { | ||
public Lookup call() throws Exception { | ||
return new Lookup(circuit.getFinalCircuitNode().getRouter(), lookup(circuit, seed)); | ||
} | ||
})); | ||
} | ||
} | ||
|
||
threadPool.awaitTermination(timeoutValue, timeoutUnit); | ||
for (ListenableFuture<Lookup> future : lookupFutures) { | ||
if (!future.isDone()) { | ||
log.warn("circuit timed out"); | ||
future.cancel(true); | ||
} | ||
} | ||
|
||
try { | ||
List<Lookup> lookups = Futures.successfulAsList(lookupFutures).get(); | ||
// Any failures will result in null entries. Remove them. | ||
lookups.removeAll(Collections.singleton(null)); | ||
|
||
// Use a map to enforce one result per exit node | ||
// TODO: randomize result selection better | ||
Map<HexDigest, InetSocketAddress> lookupMap = Maps.newHashMap(); | ||
|
||
for (Lookup lookup : lookups) { | ||
InetSocketAddress address = new InetSocketAddress(lookup.address, netParams.getPort()); | ||
lookupMap.put(lookup.router.getIdentityHash(), address); | ||
} | ||
|
||
return lookupMap.values(); | ||
} catch (ExecutionException e) { | ||
// Cannot happen, successfulAsList accepts failures | ||
throw new RuntimeException(e); | ||
} | ||
} finally { | ||
shutdownThreadPool(); | ||
} | ||
} | ||
|
||
private synchronized void shutdownThreadPool() { | ||
threadPool.shutdownNow(); | ||
threadPool = null; | ||
} | ||
|
||
private synchronized void createThreadPool(int size) { | ||
threadPool = | ||
MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(size)); | ||
} | ||
|
||
private InetAddress lookup(Circuit circuit, String seed) throws UnknownHostException { | ||
// Send a resolve cell to the exit node | ||
RelayCell cell = circuit.createRelayCell(RelayCell.RELAY_RESOLVE, RESOLVE_STREAM_ID, circuit.getFinalCircuitNode()); | ||
cell.putString(seed); | ||
circuit.sendRelayCell(cell); | ||
|
||
// Wait a few cell timeout periods (3 * 20 sec) for replies, in case the path is slow | ||
for (int i = 0 ; i < RECEIVE_RETRIES; i++) { | ||
RelayCell res = circuit.receiveRelayCell(); | ||
if (res != null) { | ||
while (res.cellBytesRemaining() > 0) { | ||
int type = res.getByte(); | ||
int len = res.getByte(); | ||
byte[] value = new byte[len]; | ||
res.getByteArray(value); | ||
int ttl = res.getInt(); | ||
|
||
if (type == RESOLVE_CNAME || type >= RESOLVE_ERROR) { | ||
// TODO handle .onion CNAME replies | ||
throw new RuntimeException(new String(value)); | ||
} else if (type == RESOLVE_IPV4 || type == RESOLVE_IPV6) { | ||
return InetAddress.getByAddress(value); | ||
} | ||
} | ||
break; | ||
} | ||
} | ||
throw new RuntimeException("Could not look up " + seed); | ||
} | ||
|
||
public synchronized void shutdown() { | ||
if (threadPool != null) { | ||
shutdownThreadPool(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
bin/ | ||
orchid-*.jar | ||
orchid-*.zip | ||
build-revision | ||
lib/xmlrpc-* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
Copyright (c) 2009-2011, Bruce Leidl | ||
All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without | ||
modification, are permitted provided that the following conditions are met: | ||
* Redistributions of source code must retain the above copyright | ||
notice, this list of conditions and the following disclaimer. | ||
* Redistributions in binary form must reproduce the above copyright | ||
notice, this list of conditions and the following disclaimer in the | ||
documentation and/or other materials provided with the distribution. | ||
* Neither the name of the author nor the | ||
names of its contributors may be used to endorse or promote products | ||
derived from this software without specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
Empty file.
Oops, something went wrong.