diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java index 4987314a36..ab0699c7f4 100644 --- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java +++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java @@ -940,7 +940,7 @@ class EstablishmentManager { } /** - * Are IP and port valid? + * Are IP and port valid? This is only for relay response. * Refuse anybody in the same /16 * @since 0.9.3 */ diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java index 2adecf5217..5cc72dfcf6 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java @@ -24,6 +24,13 @@ import net.i2p.util.SimpleTimer; * Entry points are runTest() to start a new test as Alice, * and receiveTest() for all received test packets. * + * IPv6 info: All Alice-Bob and Alice-Charlie communication is via IPv4. + * The Bob-Charlie communication may be via IPv6, however Charlie must + * be IPv4-capable. + * The IP address (of Alice) in the message must be IPv4 if present, + * as we only support testing of IPv4. + * Testing of IPv6 could be added in the future. + * * From udp.html on the website:
The automation of collaborative reachability testing for peers is
@@ -166,6 +173,8 @@ class PeerTestManager {
/**
* The next few methods are for when we are Alice
+ *
+ * @param bobIP IPv4 only
*/
public synchronized void runTest(InetAddress bobIP, int bobPort, SessionKey bobCipherKey, SessionKey bobMACKey) {
if (_currentTest != null) {
@@ -304,7 +313,7 @@ class PeerTestManager {
// The reply is from Bob
int ipSize = testInfo.readIPSize();
- if (ipSize != 4 && ipSize != 16) {
+ if (ipSize != 4) {
// There appears to be a bug where Bob is sending us a zero-length IP.
// We could proceed without setting the IP, but then when Charlie
// sends us his message, we will think we are behind a symmetric NAT
@@ -366,6 +375,8 @@ class PeerTestManager {
throw new UnknownHostException("port 0");
test.setAlicePortFromCharlie(testPort);
byte ip[] = new byte[testInfo.readIPSize()];
+ if (ip.length != 4)
+ throw new UnknownHostException("not IPv4");
testInfo.readIP(ip, 0);
InetAddress addr = InetAddress.getByAddress(ip);
test.setAliceIPFromCharlie(addr);
@@ -505,6 +516,7 @@ class PeerTestManager {
if ((testPort > 0 && (testPort < 1024 || testPort > 65535)) ||
(testIP != null &&
((!_transport.isValid(testIP)) ||
+ testIP.length != 4 ||
_context.blocklist().isBlocklisted(testIP)))) {
// spoof check, and don't respond to privileged ports
if (_log.shouldLog(Log.WARN))
@@ -641,6 +653,8 @@ class PeerTestManager {
byte aliceIPData[] = new byte[sz];
try {
testInfo.readIP(aliceIPData, 0);
+ if (sz != 4)
+ throw new UnknownHostException("not IPv4");
int alicePort = testInfo.readPort();
if (alicePort == 0)
throw new UnknownHostException("port 0");
@@ -706,7 +720,12 @@ class PeerTestManager {
PeerState charlie;
RouterInfo charlieInfo = null;
if (state == null) { // pick a new charlie
- charlie = _transport.pickTestPeer(from);
+ if (from.getIP().length != 4) {
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("PeerTest over IPv6 from Alice as Bob? " + from);
+ return;
+ }
+ charlie = _transport.pickTestPeer(CHARLIE, from);
} else {
charlie = _transport.getPeerState(new RemoteHostId(state.getCharlieIP().getAddress(), state.getCharliePort()));
}
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
index 84d3dec852..6f6b563f96 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
@@ -38,6 +38,7 @@ import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.router.transport.Transport;
import net.i2p.router.transport.TransportBid;
import net.i2p.router.transport.TransportImpl;
+import static net.i2p.router.transport.udp.PeerTestState.Role.*;
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.router.util.RandomIterator;
import net.i2p.util.Addresses;
@@ -446,8 +447,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
*/
SessionKey getIntroKey() { return _introKey; }
- public InetAddress getLocalAddress() { return _externalListenHost; }
- public int getExternalPort() { return _externalListenPort; }
+ int getExternalPort() { return _externalListenPort; }
/**
* @return IP or null
@@ -517,8 +517,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
/**
* From config, UPnP, local i/f, ...
+ * Not for info received from peers - see externalAddressReceived(Hash, ip, port)
*
- * @param source used for logging only
+ * @param source as defined in Transport.SOURCE_xxx
* @param ip publicly routable IPv4 or IPv6
* @param port 0 if unknown
*/
@@ -535,12 +536,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
return;
if (source.equals(Transport.SOURCE_INTERFACE)) {
// temp prevent multiples
- if (ip.length == 4 && !gotIPv4Addr) {
- gotIPv4Addr = true;
- return;
- } else if (ip.length == 16 && !gotIPv6Addr) {
- gotIPv6Addr = true;
- return;
+ if (ip.length == 4) {
+ if (gotIPv4Addr)
+ return;
+ else
+ gotIPv4Addr = true;
+ } else if (ip.length == 16) {
+ if (gotIPv6Addr)
+ return;
+ else
+ gotIPv6Addr = true;
}
}
boolean changed = changeAddress(ip, port);
@@ -585,6 +590,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* @param ourPort >= 1024
*/
void externalAddressReceived(Hash from, byte ourIP[], int ourPort) {
+ if (ourIP.length != 4)
+ return;
boolean isValid = isValid(ourIP) &&
((ourPort >= MIN_EXTERNAL_PORT && ourPort <= MAX_EXTERNAL_PORT) ||
ourPort == _externalListenPort || _externalListenPort <= 0);
@@ -616,27 +623,36 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// leaving us thinking the second IP is still good.
if (_log.shouldLog(Log.INFO))
_log.info("Ignoring IP address suggestion, since we have received an inbound con recently");
- } else if (from.equals(_lastFrom) || !eq(_lastOurIP, _lastOurPort, ourIP, ourPort)) {
- _lastFrom = from;
- _lastOurIP = ourIP;
- _lastOurPort = ourPort;
- if (_log.shouldLog(Log.INFO))
- _log.info("The router " + from + " told us we have a new IP - "
- + Addresses.toString(ourIP, ourPort) + ". Wait until somebody else tells us the same thing.");
} else {
- if (_log.shouldLog(Log.INFO))
- _log.info(from + " and " + _lastFrom + " agree we have a new IP - "
- + Addresses.toString(ourIP, ourPort) + ". Changing address.");
- _lastFrom = from;
- _lastOurIP = ourIP;
- _lastOurPort = ourPort;
- changeAddress(ourIP, ourPort);
+ // New IP
+ boolean changeIt = false;
+ synchronized(this) {
+ if (from.equals(_lastFrom) || !eq(_lastOurIP, _lastOurPort, ourIP, ourPort)) {
+ _lastFrom = from;
+ _lastOurIP = ourIP;
+ _lastOurPort = ourPort;
+ if (_log.shouldLog(Log.INFO))
+ _log.info("The router " + from + " told us we have a new IP - "
+ + Addresses.toString(ourIP, ourPort) + ". Wait until somebody else tells us the same thing.");
+ } else {
+ _lastFrom = from;
+ _lastOurIP = ourIP;
+ _lastOurPort = ourPort;
+ changeIt = true;
+ }
+ }
+ if (changeIt) {
+ if (_log.shouldLog(Log.INFO))
+ _log.info(from + " and " + _lastFrom + " agree we have a new IP - "
+ + Addresses.toString(ourIP, ourPort) + ". Changing address.");
+ changeAddress(ourIP, ourPort);
+ }
}
-
}
/**
* @param ourIP MUST have been previously validated with isValid()
+ * IPv4 or IPv6 OK
* @param ourPort >= 1024 or 0 for no change
*/
private boolean changeAddress(byte ourIP[], int ourPort) {
@@ -2702,24 +2718,50 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
return _reachabilityStatus;
}
+
@Override
public void recheckReachability() {
_testEvent.runTest();
}
- PeerState pickTestPeer(RemoteHostId dontInclude) {
+ /**
+ * Pick a Bob (if we are Alice) or a Charlie (if we are Bob).
+ *
+ * For Bob (as called from PeerTestEvent below), returns an established IPv4 peer.
+ * While the protocol allows Alice to select an unestablished Bob, we don't support that.
+ *
+ * For Charlie (as called from PeerTestManager), returns an established IPv4 or IPv6 peer.
+ * (doesn't matter how Bob and Charlie communicate)
+ *
+ * Any returned peer must advertise an IPv4 address to prove it is IPv4-capable.
+ *
+ * @param peerRole BOB or CHARLIE only
+ * @param dontInclude may be null
+ * @return IPv4 peer or null
+ */
+ PeerState pickTestPeer(PeerTestState.Role peerRole, RemoteHostId dontInclude) {
+ if (peerRole == ALICE)
+ throw new IllegalArgumentException();
List