forked from I2P_Developers/i2p.i2p
- Fix multiple-detection code in externalAddressReceived()
- Synchronize tracking of last IP/port - Don't accept IPv6 address changes from peers - Remove unused getLocalAddress() - Pkg private getLocalPort() Peer tests: - Use only IPv4 peer for Alice and Bob in peer tests; Charlie may be an IPv6 peer. - Enforce IPv4 (Alice's) address inside PeerTest packet
This commit is contained in:
@ -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
|
||||
*/
|
||||
|
@ -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:
|
||||
|
||||
<p>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()));
|
||||
}
|
||||
|
@ -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<PeerState> peers = new ArrayList(_peersByIdent.values());
|
||||
for (Iterator<PeerState> iter = new RandomIterator(peers); iter.hasNext(); ) {
|
||||
PeerState peer = iter.next();
|
||||
if ( (dontInclude != null) && (dontInclude.equals(peer.getRemoteHostId())) )
|
||||
continue;
|
||||
// enforce IPv4 connection for BOB
|
||||
byte[] ip = peer.getRemoteIP();
|
||||
if (peerRole == BOB && ip.length != 4)
|
||||
continue;
|
||||
// enforce IPv4 advertised for all
|
||||
RouterInfo peerInfo = _context.netDb().lookupRouterInfoLocally(peer.getRemotePeer());
|
||||
if (peerInfo == null)
|
||||
continue;
|
||||
RouterAddress addr = peerInfo.getTargetAddress(STYLE);
|
||||
if (addr == null)
|
||||
continue;
|
||||
byte[] ip = addr.getIP();
|
||||
ip = null;
|
||||
List<RouterAddress> addrs = peerInfo.getTargetAddresses(STYLE);
|
||||
for (RouterAddress addr : addrs) {
|
||||
ip = addr.getIP();
|
||||
if (ip != null && ip.length == 4)
|
||||
break;
|
||||
}
|
||||
if (ip == null)
|
||||
continue;
|
||||
if (DataHelper.eq(ip, 0, getExternalIP(), 0, 2))
|
||||
@ -2735,6 +2777,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
//return ( (val != null) && ("true".equals(val)) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate a test (we are Alice)
|
||||
*/
|
||||
private class PeerTestEvent extends SimpleTimer2.TimedEvent {
|
||||
private volatile boolean _alive;
|
||||
/** when did we last test our reachability */
|
||||
@ -2761,7 +2806,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
|
||||
private void runTest() {
|
||||
PeerState bob = pickTestPeer(null);
|
||||
PeerState bob = pickTestPeer(BOB, null);
|
||||
if (bob != null) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Running periodic test with bob = " + bob);
|
||||
|
Reference in New Issue
Block a user