Added optional "senderPeerAddress" string to HELLO messages, to allow external IP changes to be detected without using a centralized service.

This commit is contained in:
CalDescent 2022-01-03 19:05:10 +00:00
parent b1c1634950
commit f007f9a86d
3 changed files with 85 additions and 6 deletions

View File

@ -48,6 +48,9 @@ public enum Handshake {
return null;
}
// Make a note of the senderPeerAddress, as this should be our public IP
Network.getInstance().ourPeerAddressUpdated(helloMessage.getSenderPeerAddress());
String versionString = helloMessage.getVersionString();
Matcher matcher = peer.VERSION_PATTERN.matcher(versionString);
@ -87,8 +90,9 @@ public enum Handshake {
public void action(Peer peer) {
String versionString = Controller.getInstance().getVersionString();
long timestamp = NTP.getTime();
String senderPeerAddress = peer.getPeerData().getAddress().toString();
Message helloMessage = new HelloMessage(timestamp, versionString);
Message helloMessage = new HelloMessage(timestamp, versionString, senderPeerAddress);
if (!peer.sendMessage(helloMessage))
peer.disconnect("failed to send HELLO");
}

View File

@ -7,7 +7,6 @@ import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.qortal.block.BlockChain;
import org.qortal.controller.Controller;
import org.qortal.controller.arbitrary.ArbitraryDataFileManager;
import org.qortal.controller.arbitrary.ArbitraryDataManager;
import org.qortal.crypto.Crypto;
import org.qortal.data.block.BlockData;
import org.qortal.data.network.PeerData;
@ -117,6 +116,9 @@ public class Network {
private final Lock mergePeersLock = new ReentrantLock();
private List<String> ourExternalIpAddressHistory = new ArrayList<>();
private String ourExternalIpAddress = null;
// Constructors
private Network() {
@ -1102,6 +1104,65 @@ public class Network {
return new GetUnconfirmedTransactionsMessage();
}
// External IP / peerAddress tracking
public void ourPeerAddressUpdated(String peerAddress) {
if (peerAddress == null) {
return;
}
String[] parts = peerAddress.split(":");
if (parts.length != 2) {
return;
}
String host = parts[0];
try {
InetAddress addr = InetAddress.getByName(host);
if (addr.isAnyLocalAddress() || addr.isSiteLocalAddress()) {
// Ignore local addresses
return;
}
} catch (UnknownHostException e) {
return;
}
this.ourExternalIpAddressHistory.add(host);
// Limit to 10 entries
while (this.ourExternalIpAddressHistory.size() > 10) {
this.ourExternalIpAddressHistory.remove(0);
}
// If we've had 3 consecutive matching addresses, and they're different from
// our stored IP address value, treat it as updated.
int size = this.ourExternalIpAddressHistory.size();
if (size < 3) {
// Need at least 3 readings
return;
}
String ip1 = this.ourExternalIpAddressHistory.get(size - 1);
String ip2 = this.ourExternalIpAddressHistory.get(size - 2);
String ip3 = this.ourExternalIpAddressHistory.get(size - 3);
if (!Objects.equals(ip1, this.ourExternalIpAddress)) {
// Latest reading doesn't match our known value
if (Objects.equals(ip1, ip2) && Objects.equals(ip1, ip3)) {
// Last 3 readings were the same - i.e. more than one peer agreed on the new IP address
this.ourExternalIpAddress = ip1;
this.onExternalIpUpdate(ip1);
}
}
}
public void onExternalIpUpdate(String ipAddress) {
LOGGER.info("External IP address updated to {}", ipAddress);
}
// Peer-management calls
public void noteToSelf(Peer peer) {

View File

@ -13,16 +13,18 @@ public class HelloMessage extends Message {
private final long timestamp;
private final String versionString;
private final String senderPeerAddress;
private HelloMessage(int id, long timestamp, String versionString) {
private HelloMessage(int id, long timestamp, String versionString, String senderPeerAddress) {
super(id, MessageType.HELLO);
this.timestamp = timestamp;
this.versionString = versionString;
this.senderPeerAddress = senderPeerAddress;
}
public HelloMessage(long timestamp, String versionString) {
this(-1, timestamp, versionString);
public HelloMessage(long timestamp, String versionString, String senderPeerAddress) {
this(-1, timestamp, versionString, senderPeerAddress);
}
public long getTimestamp() {
@ -33,12 +35,22 @@ public class HelloMessage extends Message {
return this.versionString;
}
public String getSenderPeerAddress() {
return this.senderPeerAddress;
}
public static Message fromByteBuffer(int id, ByteBuffer byteBuffer) throws TransformationException {
long timestamp = byteBuffer.getLong();
String versionString = Serialization.deserializeSizedString(byteBuffer, 255);
return new HelloMessage(id, timestamp, versionString);
// Sender peer address added in v3.0, so is an optional field. Older versions won't send it.
String senderPeerAddress = null;
if (byteBuffer.hasRemaining()) {
senderPeerAddress = Serialization.deserializeSizedString(byteBuffer, 255);
}
return new HelloMessage(id, timestamp, versionString, senderPeerAddress);
}
@Override
@ -49,6 +61,8 @@ public class HelloMessage extends Message {
Serialization.serializeSizedString(bytes, this.versionString);
Serialization.serializeSizedString(bytes, this.senderPeerAddress);
return bytes.toByteArray();
}