forked from bitcoinj/bitcoinj
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Changes to support connecting to .onion addresses:
+ PeerAddress detects .onion and serializes/deserializes them using the onioncat format, which is also used by bitcoin-core, btcd, and probably others. + added some new DNS seeds from bitcoin-core + PeerGroup now listens for "addr" protocol messages and adds new peers to inactive list. This enables peer discovery beyond what is found by DNS and hard-coded seeds. Discovered peers are not presently persisted to disk. + Beginnings of a class for validating that peer addresses are routable. + Catch PeerDiscoveryException during getPeers call. avoids stack trace when all DNS lookups timeout Cherry pick bisq-network@d8a5d08
- Loading branch information
1 parent
1f97c71
commit c342f5d
Showing
6 changed files
with
446 additions
and
21 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
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,71 @@ | ||
package org.bitcoinj.net; | ||
|
||
import org.bitcoinj.utils.CIDRUtils; | ||
|
||
import java.net.InetAddress; | ||
|
||
/** | ||
* Created by danda on 1/12/17. | ||
*/ | ||
public class AddressChecker { | ||
|
||
private CIDRUtils onionCatNet; | ||
private CIDRUtils rfc4193Net; | ||
|
||
public AddressChecker() { | ||
|
||
// Note: this is borrowed/ported from btcd (written in go). | ||
|
||
// btcd has many more rules that are probably important and should be | ||
// implemented in this class, but for now we only care about onion | ||
// addresses for onioncat (ipv6) encoding/decoding. | ||
|
||
// onionCatNet defines the IPv6 address block used to support Tor. | ||
// bitcoind encodes a .onion address as a 16 byte number by decoding the | ||
// address prior to the .onion (i.e. the key hash) base32 into a ten | ||
// byte number. It then stores the first 6 bytes of the address as | ||
// 0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43. | ||
// | ||
// This is the same range used by OnionCat, which is part part of the | ||
// RFC4193 unique local IPv6 range. | ||
// | ||
// In summary the format is: | ||
// { magic 6 bytes, 10 bytes base32 decode of key hash } | ||
onionCatNet = new CIDRUtils("fd87:d87e:eb43::", 48); | ||
|
||
// rfc4193Net specifies the IPv6 unique local address block as defined | ||
// by RFC4193 (FC00::/7). | ||
rfc4193Net = new CIDRUtils("FC00::", 7); | ||
} | ||
|
||
// IsValid returns whether or not the passed address is valid. The address is | ||
// considered invalid under the following circumstances: | ||
// IPv4: It is either a zero or all bits set address. | ||
// IPv6: It is either a zero or RFC3849 documentation address. | ||
public boolean IsValid(InetAddress addr) { | ||
// todo: port/implement. | ||
|
||
// IsUnspecified returns if address is 0, so only all bits set, and | ||
// RFC3849 need to be explicitly checked. | ||
|
||
// return na.IP != nil && !(na.IP.IsUnspecified() || | ||
// na.IP.Equal(net.IPv4bcast)) | ||
|
||
return true; | ||
} | ||
|
||
// IsOnionCatTor returns whether or not the passed address is in the IPv6 range | ||
// used by bitcoin to support Tor (fd87:d87e:eb43::/48). Note that this range | ||
// is the same range used by OnionCat, which is part of the RFC4193 unique local | ||
// IPv6 range. | ||
public boolean IsOnionCatTor(InetAddress addr) { | ||
return onionCatNet.isInRange(addr); | ||
} | ||
|
||
// IsRFC4193 returns whether or not the passed address is part of the IPv6 | ||
// unique local range as defined by RFC4193 (FC00::/7). | ||
public boolean IsRFC4193(InetAddress addr) { | ||
return rfc4193Net.isInRange(addr); | ||
} | ||
|
||
} |
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,53 @@ | ||
package org.bitcoinj.net; | ||
|
||
import org.bitcoinj.utils.Base32; | ||
|
||
import java.net.InetAddress; | ||
import java.net.InetSocketAddress; | ||
import java.net.UnknownHostException; | ||
import java.util.Arrays; | ||
|
||
|
||
/** | ||
* Created by danda on 1/12/17. | ||
*/ | ||
public class OnionCat { | ||
|
||
/** Converts a .onion address to onioncat format | ||
* | ||
* @param hostname | ||
* @return | ||
*/ | ||
public static byte[] onionHostToIPV6Bytes(String hostname) { | ||
String needle = ".onion"; | ||
if( hostname.endsWith(needle) ) { | ||
hostname = hostname.substring(0,hostname.length() - needle.length()); | ||
} | ||
byte[] prefix = new byte[] {(byte)0xfd, (byte)0x87, (byte)0xd8, (byte)0x7e, (byte)0xeb, (byte)0x43}; | ||
byte[] onionaddr = Base32.base32Decode(hostname); | ||
byte[] ipBytes = new byte[prefix.length + onionaddr.length]; | ||
System.arraycopy(prefix, 0, ipBytes, 0, prefix.length); | ||
System.arraycopy(onionaddr, 0, ipBytes, prefix.length, onionaddr.length); | ||
|
||
return ipBytes; | ||
} | ||
|
||
public static InetAddress onionHostToInetAddress(String hostname) throws UnknownHostException { | ||
return InetAddress.getByAddress(onionHostToIPV6Bytes(hostname)); | ||
} | ||
|
||
public static InetSocketAddress onionHostToInetSocketAddress(String hostname, int port) throws UnknownHostException { | ||
return new InetSocketAddress( onionHostToInetAddress(hostname), port); | ||
} | ||
|
||
|
||
/** Converts an IPV6 onioncat encoded address to a hostname | ||
* | ||
* @param bytes | ||
* @return | ||
*/ | ||
public static String IPV6BytesToOnionHost( byte[] bytes) { | ||
String base32 = Base32.base32Encode( Arrays.copyOfRange(bytes, 6, 16) ); | ||
return base32.toLowerCase() + ".onion"; | ||
} | ||
} |
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,90 @@ | ||
// Copied from orchid Tor lib. | ||
|
||
package org.bitcoinj.utils; | ||
|
||
import com.subgraph.orchid.TorException; | ||
|
||
public class Base32 { | ||
private final static String BASE32_CHARS = "abcdefghijklmnopqrstuvwxyz234567"; | ||
|
||
public static String base32Encode(byte[] source) { | ||
return base32Encode(source, 0, source.length); | ||
} | ||
|
||
public static String base32Encode(byte[] source, int offset, int length) { | ||
final int nbits = length * 8; | ||
if(nbits % 5 != 0) | ||
throw new TorException("Base32 input length must be a multiple of 5 bits"); | ||
|
||
final int outlen = nbits / 5; | ||
final StringBuffer outbuffer = new StringBuffer(); | ||
int bit = 0; | ||
for(int i = 0; i < outlen; i++) { | ||
int v = (source[bit / 8] & 0xFF) << 8; | ||
if(bit + 5 < nbits) v += (source[bit / 8 + 1] & 0xFF); | ||
int u = (v >> (11 - (bit % 8))) & 0x1F; | ||
outbuffer.append(BASE32_CHARS.charAt(u)); | ||
bit += 5; | ||
} | ||
return outbuffer.toString(); | ||
} | ||
|
||
public static byte[] base32Decode(String source) { | ||
int[] v = stringToIntVector(source); | ||
|
||
int nbits = source.length() * 5; | ||
if(nbits % 8 != 0) | ||
throw new TorException("Base32 decoded array must be a muliple of 8 bits"); | ||
|
||
int outlen = nbits / 8; | ||
byte[] outbytes = new byte[outlen]; | ||
|
||
int bit = 0; | ||
for(int i = 0; i < outlen; i++) { | ||
int bb = bit / 5; | ||
outbytes[i] = (byte) decodeByte(bit, v[bb], v[bb + 1], v[bb + 2]); | ||
bit += 8; | ||
} | ||
return outbytes; | ||
} | ||
|
||
private static int decodeByte(int bitOffset, int b0, int b1, int b2) { | ||
switch(bitOffset % 40) { | ||
case 0: | ||
return ls(b0, 3) + rs(b1, 2); | ||
case 8: | ||
return ls(b0, 6) + ls(b1, 1) + rs (b2, 4); | ||
case 16: | ||
return ls(b0, 4) + rs(b1, 1); | ||
case 24: | ||
return ls(b0, 7) + ls(b1, 2) + rs(b2, 3); | ||
case 32: | ||
return ls(b0, 5) + (b1 & 0xFF); | ||
} | ||
throw new TorException("Illegal bit offset"); | ||
} | ||
|
||
private static int ls(int n, int shift) { | ||
return ((n << shift) & 0xFF); | ||
} | ||
|
||
private static int rs(int n, int shift) { | ||
return ((n >> shift) & 0xFF); | ||
} | ||
|
||
private static int[] stringToIntVector(String s) { | ||
final int[] ints = new int[s.length() + 1]; | ||
for(int i = 0; i < s.length(); i++) { | ||
int b = s.charAt(i) & 0xFF; | ||
if(b > 0x60 && b < 0x7B) | ||
ints[i] = b - 0x61; | ||
else if(b > 0x31 && b < 0x38) | ||
ints[i] = b - 0x18; | ||
else if(b > 0x40 && b < 0x5B) | ||
ints[i] = b - 0x41; | ||
else | ||
throw new TorException("Illegal character in base32 encoded string: "+ s.charAt(i)); | ||
} | ||
return ints; | ||
} | ||
} |
Oops, something went wrong.