Skip to content

Commit

Permalink
PeerAddress: Use addr and hostname as alternatives.
Browse files Browse the repository at this point in the history
  • Loading branch information
oscarguindzberg committed Mar 22, 2019
1 parent 3cf3edd commit b431458
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 28 deletions.
62 changes: 34 additions & 28 deletions core/src/main/java/org/bitcoinj/core/PeerAddress.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ public class PeerAddress extends ChildMessage {

static final int MESSAGE_SIZE = 30;

//addr and hostname are alternatives and should not be used together.
private InetAddress addr;
private String hostname; // Used for .onion addresses
private String hostname;

private int port;
private BigInteger services;
private long time;
Expand Down Expand Up @@ -120,21 +122,11 @@ public PeerAddress(NetworkParameters params, InetAddress addr) {
* Protocol version is the default for Bitcoin.
*/
public PeerAddress(InetSocketAddress addr) {
/* socks addresses, eg Tor, use hostname only because no local lookup is performed.
* includes .onion hidden services.
*/
String host = addr.getHostString();
if( host != null && host.endsWith(".onion") ) {
this.hostname = host;
try {
this.addr = OnionCatConverter.onionHostToInetAddress(this.hostname);
} catch (UnknownHostException e) {
// No dns lookup is performed because InetAddress method is supposed to be invoked with numeric address
// parameter, so this is unreachable code.
throw new RuntimeException(e);
}
InetAddress inetAddress = addr.getAddress();
if(inetAddress != null) {
this.addr = inetAddress;
} else {
this.addr = checkNotNull(addr.getAddress());
this.hostname = checkNotNull(addr.getHostString());
}
this.port = addr.getPort();
this.protocolVersion = NetworkParameters.ProtocolVersion.CURRENT.getBitcoinProtocolVersion();
Expand All @@ -147,11 +139,21 @@ public PeerAddress(InetSocketAddress addr) {
* InetAddress or a String hostname. If you want to connect to a .onion, set the hostname to the .onion address.
*/
public PeerAddress(NetworkParameters params, InetSocketAddress addr) {
this(params, addr.getAddress(), addr.getPort());
super(params);
InetAddress inetAddress = addr.getAddress();
if(inetAddress != null) {
this.addr = inetAddress;
} else {
this.hostname = checkNotNull(addr.getHostString());
}
this.port = addr.getPort();
this.protocolVersion = params.getProtocolVersionNum(NetworkParameters.ProtocolVersion.CURRENT);
this.services = BigInteger.ZERO;
length = protocolVersion > 31402 ? MESSAGE_SIZE : MESSAGE_SIZE - 4;
}

/**
* Constructs a peer address from a stringified hostname+port. Use this if you want to connect to a Tor .onion address.
* Constructs a peer address from a stringified hostname+port.
* Protocol version is the default for Bitcoin.
*/
public PeerAddress(String hostname, int port) {
Expand All @@ -162,7 +164,7 @@ public PeerAddress(String hostname, int port) {
}

/**
* Constructs a peer address from a stringified hostname+port. Use this if you want to connect to a Tor .onion address.
* Constructs a peer address from a stringified hostname+port.
*/
public PeerAddress(NetworkParameters params, String hostname, int port) {
super(params);
Expand All @@ -188,8 +190,12 @@ protected void bitcoinSerializeToStream(OutputStream stream) throws IOException
uint64ToByteStreamLE(services, stream); // nServices.

byte[] ipBytes;
if (OnionCatAddressChecker.isOnionCatTor(addr)) {
ipBytes = OnionCatConverter.onionHostToIPV6Bytes(hostname);
if (hostname!=null) {
if (hostname.endsWith(".onion")) {
ipBytes = OnionCatConverter.onionHostToIPV6Bytes(hostname);
} else {
ipBytes = new byte[16];
}
} else if( addr != null ) {
// Java does not provide any utility to map an IPv4 address into IPv6 space, so we have to do it by hand.
ipBytes = addr.getAddress();
Expand All @@ -199,11 +205,9 @@ protected void bitcoinSerializeToStream(OutputStream stream) throws IOException
v6addr[10] = (byte) 0xFF;
v6addr[11] = (byte) 0xFF;
ipBytes = v6addr;
} else {
ipBytes = new byte[16];
}
} else {
ipBytes = new byte[16]; // zero-filled.
throw new IllegalStateException("Either hostname or addr should be not null");
}
stream.write(ipBytes);
// And write out the port. Unlike the rest of the protocol, address and port is in big endian byte order.
Expand All @@ -224,15 +228,17 @@ protected void parse() throws ProtocolException {
time = -1;
services = readUint64();
byte[] addrBytes = readBytes(16);
InetAddress inetAddress;
try {
addr = InetAddress.getByAddress(addrBytes);

if( OnionCatAddressChecker.isOnionCatTor( addr )) {
hostname = OnionCatConverter.IPV6BytesToOnionHost( addr.getAddress() );
}
inetAddress = InetAddress.getByAddress(addrBytes);
} catch (UnknownHostException e) {
throw new RuntimeException(e); // Cannot happen.
}
if(OnionCatAddressChecker.isOnionCatTor(inetAddress)) {
hostname = OnionCatConverter.IPV6BytesToOnionHost(inetAddress.getAddress());
} else {
addr = inetAddress;
}
port = ((0xFF & payload[cursor++]) << 8) | (0xFF & payload[cursor++]);
// The 4 byte difference is the uint32 timestamp that was introduced in version 31402
length = protocolVersion > 31402 ? MESSAGE_SIZE : MESSAGE_SIZE - 4;
Expand Down
29 changes: 29 additions & 0 deletions core/src/test/java/org/bitcoinj/core/PeerAddressTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.junit.Test;

import java.net.InetAddress;
import java.net.InetSocketAddress;

import static org.bitcoinj.core.Utils.HEX;
import static org.junit.Assert.assertEquals;
Expand All @@ -43,4 +44,32 @@ public void testBitcoinSerialize() throws Exception {
assertEquals("000000000000000000000000000000000000ffff7f000001208d",
Utils.HEX.encode(pa.bitcoinSerialize()));
}

@Test
public void testOnionHostname() throws Exception {
PeerAddress pa = new PeerAddress(InetSocketAddress.createUnresolved("explorernuoc63nb.onion", 8333));
assertEquals("explorernuoc63nb.onion", pa.toSocketAddress().getHostString());
assertEquals("explorernuoc63nb.onion", pa.getHostname());
assertEquals(null, pa.getAddr());
assertEquals(8333, pa.toSocketAddress().getPort());
assertEquals(8333, pa.getPort());
PeerAddress pa2 = new PeerAddress(MainNetParams.get(), InetSocketAddress.createUnresolved("explorernuoc63nb.onion", 8333));
assertPeerAddressEqualsRegardlessOfTime(pa, pa2);
PeerAddress pa3 = new PeerAddress("explorernuoc63nb.onion", 8333);
assertPeerAddressEqualsRegardlessOfTime(pa, pa3);
PeerAddress pa4 = new PeerAddress(MainNetParams.get(), "explorernuoc63nb.onion", 8333);
assertPeerAddressEqualsRegardlessOfTime(pa, pa4);
byte[] serialized = pa.unsafeBitcoinSerialize();
PeerAddress paFromSerialized = new PeerAddress(MainNetParams.get(), serialized, 0, NetworkParameters.ProtocolVersion.CURRENT.getBitcoinProtocolVersion());
assertPeerAddressEqualsRegardlessOfTime(pa, paFromSerialized);
}

private void assertPeerAddressEqualsRegardlessOfTime(PeerAddress pa, PeerAddress pa2) {
assertEquals(pa.getPort(), pa2.getPort());
assertEquals(pa.getAddr(), pa2.getAddr());
assertEquals(pa.getHostname(), pa2.getHostname());
assertEquals(pa.getServices(), pa2.getServices());
}


}

0 comments on commit b431458

Please sign in to comment.