forked from I2P_Developers/i2p.i2p
* SSU:
- Fail establishment immediately on SessionCreated
validation fail
- Defer outbound DH generation until required
- Validate address/port in RelayIntro messages
- Throttle hole punches
- More cleanups
This commit is contained in:
12
history.txt
12
history.txt
@@ -1,3 +1,15 @@
|
||||
2012-08-22 zzz
|
||||
* NetDB: Add hash collision detection
|
||||
* SimpleTimer2: Synchronization improvements (ticket #653)
|
||||
* SSU:
|
||||
- Fail establishment immediately on SessionCreated
|
||||
validation fail
|
||||
- Defer outbound DH generation until required
|
||||
- Validate address/port in RelayIntro messages
|
||||
- Throttle hole punches
|
||||
- Workaround for Android ICS bug
|
||||
- More cleanups
|
||||
|
||||
2012-08-21 zzz
|
||||
* NetDB: Decrease stat publish probability
|
||||
* SSU:
|
||||
|
||||
@@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 14;
|
||||
public final static long BUILD = 15;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
||||
@@ -340,7 +340,7 @@ class EstablishmentManager {
|
||||
}
|
||||
state = new OutboundEstablishState(_context, maybeTo, to,
|
||||
toIdentity,
|
||||
sessionKey, addr, _transport.getDHBuilder());
|
||||
sessionKey, addr, _transport.getDHFactory());
|
||||
OutboundEstablishState oldState = _outboundStates.putIfAbsent(to, state);
|
||||
boolean isNew = oldState == null;
|
||||
if (isNew) {
|
||||
@@ -394,6 +394,15 @@ class EstablishmentManager {
|
||||
return getMaxConcurrentEstablish()/2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we allow another inbound establishment?
|
||||
* Used to throttle outbound hole punches.
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public boolean shouldAllowInboundEstablishment() {
|
||||
return _inboundStates.size() < getMaxInboundEstablishers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Got a SessionRequest (initiates an inbound establishment)
|
||||
*
|
||||
@@ -405,15 +414,13 @@ class EstablishmentManager {
|
||||
return;
|
||||
}
|
||||
|
||||
int maxInbound = getMaxInboundEstablishers();
|
||||
|
||||
boolean isNew = false;
|
||||
|
||||
InboundEstablishState state = _inboundStates.get(from);
|
||||
if (state == null) {
|
||||
// TODO this is insufficient to prevent DoSing, especially if
|
||||
// IP spoofing is used. For further study.
|
||||
if (_inboundStates.size() >= maxInbound) {
|
||||
if (!shouldAllowInboundEstablishment()) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Dropping inbound establish, increase " + PROP_MAX_CONCURRENT_ESTABLISH);
|
||||
_context.statManager().addRateData("udp.establishDropped", 1);
|
||||
@@ -861,10 +868,15 @@ class EstablishmentManager {
|
||||
state.setIntroNonce(nonce);
|
||||
}
|
||||
_context.statManager().addRateData("udp.sendIntroRelayRequest", 1, 0);
|
||||
UDPPacket requests[] = _builder.buildRelayRequest(_transport, state, _transport.getIntroKey());
|
||||
for (int i = 0; i < requests.length; i++) {
|
||||
if (requests[i] != null)
|
||||
_transport.send(requests[i]);
|
||||
List<UDPPacket> requests = _builder.buildRelayRequest(_transport, state, _transport.getIntroKey());
|
||||
if (requests.isEmpty()) {
|
||||
// FIXME need a failed OB state
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No valid introducers! " + state);
|
||||
// set failed state, remove nonce, and return
|
||||
}
|
||||
for (UDPPacket req : requests) {
|
||||
_transport.send(req);
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Send intro for " + state + " with our intro key as " + _transport.getIntroKey());
|
||||
@@ -895,8 +907,9 @@ class EstablishmentManager {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Introducer for " + state + " (" + bob + ") sent us an invalid address for our target: " + Addresses.toString(ip, port), uhe);
|
||||
// these two cause this peer to requeue for a new intro peer
|
||||
state.introductionFailed();
|
||||
notifyActivity();
|
||||
// FIXME no it doesn't, we send to all at once
|
||||
//state.introductionFailed();
|
||||
//notifyActivity();
|
||||
return;
|
||||
}
|
||||
_context.statManager().addRateData("udp.receiveIntroRelayResponse", state.getLifetime(), 0);
|
||||
@@ -936,7 +949,7 @@ class EstablishmentManager {
|
||||
boolean valid = state.validateSessionCreated();
|
||||
if (!valid) {
|
||||
// validate clears fields on failure
|
||||
// TODO - send destroy? shitlist?
|
||||
// sendDestroy(state) won't work as we haven't sent the confirmed...
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("SessionCreated validate failed: " + state);
|
||||
return;
|
||||
@@ -1018,16 +1031,16 @@ class EstablishmentManager {
|
||||
// completely received (though the signature may be invalid)
|
||||
iter.remove();
|
||||
inboundState = cur;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Removing completely confirmed inbound state");
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Removing completely confirmed inbound state");
|
||||
break;
|
||||
} else if (cur.getLifetime() > MAX_IB_ESTABLISH_TIME) {
|
||||
// took too long
|
||||
iter.remove();
|
||||
inboundState = cur;
|
||||
//_context.statManager().addRateData("udp.inboundEstablishFailedState", cur.getState(), cur.getLifetime());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Removing expired inbound state");
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Removing expired inbound state");
|
||||
expired = true;
|
||||
break;
|
||||
} else if (cur.getState() == IB_STATE_FAILED) {
|
||||
@@ -1133,20 +1146,19 @@ class EstablishmentManager {
|
||||
|
||||
for (Iterator<OutboundEstablishState> iter = _outboundStates.values().iterator(); iter.hasNext(); ) {
|
||||
OutboundEstablishState cur = iter.next();
|
||||
if (cur.getState() == OB_STATE_CONFIRMED_COMPLETELY) {
|
||||
// completely received
|
||||
OutboundEstablishState.OutboundState state = cur.getState();
|
||||
if (state == OB_STATE_CONFIRMED_COMPLETELY ||
|
||||
state == OB_STATE_VALIDATION_FAILED) {
|
||||
iter.remove();
|
||||
outboundState = cur;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Removing confirmed outbound: " + cur);
|
||||
break;
|
||||
} else if (cur.getLifetime() >= MAX_OB_ESTABLISH_TIME) {
|
||||
// took too long
|
||||
iter.remove();
|
||||
outboundState = cur;
|
||||
//_context.statManager().addRateData("udp.outboundEstablishFailedState", cur.getState(), cur.getLifetime());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Removing expired outbound: " + cur);
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Removing expired outbound: " + cur);
|
||||
break;
|
||||
} else {
|
||||
if (cur.getNextSendTime() <= now) {
|
||||
@@ -1233,6 +1245,10 @@ class EstablishmentManager {
|
||||
else if (outboundState.getNextSendTime() <= now)
|
||||
handlePendingIntro(outboundState);
|
||||
break;
|
||||
|
||||
case OB_STATE_VALIDATION_FAILED:
|
||||
processExpired(outboundState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -178,8 +178,6 @@ class InboundEstablishState {
|
||||
/** what port number do they appear to be coming from? */
|
||||
public int getSentPort() { return _alicePort; }
|
||||
|
||||
public synchronized byte[] getBobIP() { return _bobIP; }
|
||||
|
||||
public synchronized byte[] getSentY() {
|
||||
if (_sentY == null)
|
||||
_sentY = _keyBuilder.getMyPublicValueBytes();
|
||||
@@ -328,6 +326,8 @@ class InboundEstablishState {
|
||||
|
||||
/**
|
||||
* Who is Alice (null if forged/unknown)
|
||||
*
|
||||
* Note that this isn't really confirmed - see below.
|
||||
*/
|
||||
public synchronized RouterIdentity getConfirmedIdentity() {
|
||||
if (!_verificationAttempted) {
|
||||
@@ -341,8 +341,13 @@ class InboundEstablishState {
|
||||
* Determine if Alice sent us a valid confirmation packet. The
|
||||
* identity signs: Alice's IP + Alice's port + Bob's IP + Bob's port
|
||||
* + Alice's new relay key + Alice's signed on time
|
||||
*
|
||||
* Note that the protocol does not include a signature of the RouterIdentity,
|
||||
* which could be a problem?
|
||||
*
|
||||
* Caller must synch on this.
|
||||
*/
|
||||
private synchronized void verifyIdentity() {
|
||||
private void verifyIdentity() {
|
||||
int identSize = 0;
|
||||
for (int i = 0; i < _receivedIdentity.length; i++)
|
||||
identSize += _receivedIdentity[i].length;
|
||||
@@ -385,6 +390,8 @@ class InboundEstablishState {
|
||||
Signature sig = new Signature(_receivedSignature);
|
||||
boolean ok = _context.dsa().verifySignature(sig, signed, peer.getSigningPublicKey());
|
||||
if (ok) {
|
||||
// todo partial spoof detection - get peer.calculateHash(),
|
||||
// lookup in netdb locally, if not equal, fail?
|
||||
_receivedConfirmedIdentity = peer;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@@ -408,10 +415,10 @@ class InboundEstablishState {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
buf.append("IES ");
|
||||
buf.append(Addresses.toString(_aliceIP, _alicePort));
|
||||
if (_receivedX != null)
|
||||
buf.append(" ReceivedX: ").append(Base64.encode(_receivedX, 0, 4));
|
||||
if (_sentY != null)
|
||||
buf.append(" SentY: ").append(Base64.encode(_sentY, 0, 4));
|
||||
//if (_receivedX != null)
|
||||
// buf.append(" ReceivedX: ").append(Base64.encode(_receivedX, 0, 4));
|
||||
//if (_sentY != null)
|
||||
// buf.append(" SentY: ").append(Base64.encode(_sentY, 0, 4));
|
||||
//buf.append(" Bob: ").append(Addresses.toString(_bobIP, _bobPort));
|
||||
buf.append(" RelayTag: ").append(_sentRelayTag);
|
||||
//buf.append(" SignedOn: ").append(_sentSignedOnTime);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package net.i2p.router.transport.udp;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
@@ -18,7 +21,7 @@ import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
* Keep track of inbound and outbound introductions.
|
||||
*/
|
||||
class IntroductionManager {
|
||||
private final RouterContext _context;
|
||||
@@ -29,6 +32,8 @@ class IntroductionManager {
|
||||
private final Map<Long, PeerState> _outbound;
|
||||
/** list of peers (PeerState) who have given us introduction tags */
|
||||
private final Set<PeerState> _inbound;
|
||||
private final Set<InetAddress> _recentHolePunches;
|
||||
private long _lastHolePunchClean;
|
||||
|
||||
/**
|
||||
* Limit since we ping to keep the conn open
|
||||
@@ -42,6 +47,11 @@ class IntroductionManager {
|
||||
*/
|
||||
private static final int MAX_OUTBOUND = 100;
|
||||
|
||||
/** Max one per target in this time */
|
||||
private static final long PUNCH_CLEAN_TIME = 5*1000;
|
||||
/** Max for all targets per PUNCH_CLEAN_TIME */
|
||||
private static final int MAX_PUNCHES = 8;
|
||||
|
||||
public IntroductionManager(RouterContext ctx, UDPTransport transport) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(IntroductionManager.class);
|
||||
@@ -49,6 +59,7 @@ class IntroductionManager {
|
||||
_builder = new PacketBuilder(ctx, transport);
|
||||
_outbound = new ConcurrentHashMap(MAX_OUTBOUND);
|
||||
_inbound = new ConcurrentHashSet(MAX_INBOUND);
|
||||
_recentHolePunches = new HashSet(16);
|
||||
ctx.statManager().createRateStat("udp.receiveRelayIntro", "How often we get a relayed request for us to talk to someone?", "udp", UDPTransport.RATES);
|
||||
ctx.statManager().createRateStat("udp.receiveRelayRequest", "How often we receive a good request to relay to someone else?", "udp", UDPTransport.RATES);
|
||||
ctx.statManager().createRateStat("udp.receiveRelayRequestBadTag", "Received relay requests with bad/expired tag", "udp", UDPTransport.RATES);
|
||||
@@ -203,18 +214,87 @@ class IntroductionManager {
|
||||
void receiveRelayIntro(RemoteHostId bob, UDPPacketReader reader) {
|
||||
if (_context.router().isHidden())
|
||||
return;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Receive relay intro from " + bob);
|
||||
_context.statManager().addRateData("udp.receiveRelayIntro", 1, 0);
|
||||
|
||||
if (!_transport.allowConnection())
|
||||
if (!_transport.allowConnection()) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Dropping RelayIntro, over conn limit");
|
||||
return;
|
||||
}
|
||||
|
||||
int ipSize = reader.getRelayIntroReader().readIPSize();
|
||||
byte ip[] = new byte[ipSize];
|
||||
reader.getRelayIntroReader().readIP(ip, 0);
|
||||
int port = reader.getRelayIntroReader().readPort();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Receive relay intro from " + bob + " for " + Addresses.toString(ip, port));
|
||||
|
||||
InetAddress to = null;
|
||||
try {
|
||||
if (!_transport.isValid(ip))
|
||||
throw new UnknownHostException("non-public IP");
|
||||
if (port <= 0 || port > 65535)
|
||||
throw new UnknownHostException("bad port " + port);
|
||||
to = InetAddress.getByAddress(ip);
|
||||
} catch (UnknownHostException uhe) {
|
||||
// shitlist Bob?
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("IP for alice to hole punch to is invalid", uhe);
|
||||
return;
|
||||
}
|
||||
|
||||
RemoteHostId alice = new RemoteHostId(ip, port);
|
||||
if (_transport.getPeerState(alice) != null) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Ignoring RelayIntro, already have a session to " + to);
|
||||
return;
|
||||
}
|
||||
EstablishmentManager establisher = _transport.getEstablisher();
|
||||
if (establisher != null) {
|
||||
if (establisher.getInboundState(alice) != null) {
|
||||
// This check may be common, as Alice sends RelayRequests to
|
||||
// several introducers at once.
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Ignoring RelayIntro, establishment in progress to " + to);
|
||||
return;
|
||||
}
|
||||
if (!establisher.shouldAllowInboundEstablishment()) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Dropping RelayIntro, too many establishments in progress - for " + to);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO throttle
|
||||
// TODO IB req limits
|
||||
// TODO check if already have a session or in progress state.
|
||||
// basic throttle, don't bother saving per-peer send times
|
||||
// we throttle on IP only, ignoring port
|
||||
boolean tooMany = false;
|
||||
boolean already = false;
|
||||
synchronized (_recentHolePunches) {
|
||||
long now = _context.clock().now();
|
||||
if (now > _lastHolePunchClean + PUNCH_CLEAN_TIME) {
|
||||
_recentHolePunches.clear();
|
||||
_lastHolePunchClean = now;
|
||||
_recentHolePunches.add(to);
|
||||
} else {
|
||||
tooMany = _recentHolePunches.size() >= MAX_PUNCHES;
|
||||
if (!tooMany)
|
||||
already = !_recentHolePunches.add(to);
|
||||
}
|
||||
}
|
||||
if (tooMany) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Dropping - too many - RelayIntro for " + to);
|
||||
return;
|
||||
}
|
||||
if (already) {
|
||||
// This check will trigger a lot, as Alice sends RelayRequests to
|
||||
// several introducers at once.
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Ignoring dup RelayIntro for " + to);
|
||||
return;
|
||||
}
|
||||
|
||||
_transport.send(_builder.buildHolePunch(reader));
|
||||
_transport.send(_builder.buildHolePunch(to, port));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,10 +26,11 @@ class OutboundEstablishState {
|
||||
private final RouterContext _context;
|
||||
private final Log _log;
|
||||
// SessionRequest message
|
||||
private final byte _sentX[];
|
||||
private byte _sentX[];
|
||||
private byte _bobIP[];
|
||||
private int _bobPort;
|
||||
private final DHSessionKeyBuilder _keyBuilder;
|
||||
private final DHSessionKeyBuilder.Factory _keyFactory;
|
||||
private DHSessionKeyBuilder _keyBuilder;
|
||||
// SessionCreated message
|
||||
private byte _receivedY[];
|
||||
private byte _aliceIP[];
|
||||
@@ -82,7 +83,9 @@ class OutboundEstablishState {
|
||||
/** we need to have someone introduce us to the peer, but haven't received a RelayResponse yet */
|
||||
OB_STATE_PENDING_INTRO,
|
||||
/** RelayResponse received */
|
||||
OB_STATE_INTRODUCED
|
||||
OB_STATE_INTRODUCED,
|
||||
/** SessionConfirmed failed validation */
|
||||
OB_STATE_VALIDATION_FAILED
|
||||
}
|
||||
|
||||
/** basic delay before backoff */
|
||||
@@ -99,7 +102,7 @@ class OutboundEstablishState {
|
||||
public OutboundEstablishState(RouterContext ctx, RemoteHostId claimedAddress,
|
||||
RemoteHostId remoteHostId,
|
||||
RouterIdentity remotePeer, SessionKey introKey, UDPAddress addr,
|
||||
DHSessionKeyBuilder dh) {
|
||||
DHSessionKeyBuilder.Factory dh) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(OutboundEstablishState.class);
|
||||
if (claimedAddress != null) {
|
||||
@@ -117,9 +120,7 @@ class OutboundEstablishState {
|
||||
_establishBegin = ctx.clock().now();
|
||||
_remoteAddress = addr;
|
||||
_introductionNonce = -1;
|
||||
_keyBuilder = dh;
|
||||
_sentX = new byte[UDPPacketReader.SessionRequestReader.X_LENGTH];
|
||||
prepareSessionRequest();
|
||||
_keyFactory = dh;
|
||||
if (addr.getIntroducerCount() > 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("new outbound establish to " + remotePeer.calculateHash() + ", with address: " + addr);
|
||||
@@ -165,8 +166,10 @@ class OutboundEstablishState {
|
||||
public RouterIdentity getRemoteIdentity() { return _remotePeer; }
|
||||
public SessionKey getIntroKey() { return _introKey; }
|
||||
|
||||
/** called from constructor, no need to synch */
|
||||
/** caller must synch - only call once */
|
||||
private void prepareSessionRequest() {
|
||||
_keyBuilder = _keyFactory.getBuilder();
|
||||
_sentX = new byte[UDPPacketReader.SessionRequestReader.X_LENGTH];
|
||||
byte X[] = _keyBuilder.getMyPublicValue().toByteArray();
|
||||
if (X.length == 257)
|
||||
System.arraycopy(X, 1, _sentX, 0, _sentX.length);
|
||||
@@ -176,7 +179,13 @@ class OutboundEstablishState {
|
||||
System.arraycopy(X, 0, _sentX, _sentX.length - X.length, X.length);
|
||||
}
|
||||
|
||||
public byte[] getSentX() { return _sentX; }
|
||||
public synchronized byte[] getSentX() {
|
||||
// We defer keygen until now so that it gets done in the Establisher loop,
|
||||
// and so that we don't waste entropy on failed introductions
|
||||
if (_sentX == null)
|
||||
prepareSessionRequest();
|
||||
return _sentX;
|
||||
}
|
||||
|
||||
/**
|
||||
* The remote side (Bob) - note that in some places he's called Charlie.
|
||||
@@ -191,6 +200,11 @@ class OutboundEstablishState {
|
||||
public synchronized int getSentPort() { return _bobPort; }
|
||||
|
||||
public synchronized void receiveSessionCreated(UDPPacketReader.SessionCreatedReader reader) {
|
||||
if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Session created already failed");
|
||||
return;
|
||||
}
|
||||
if (_receivedY != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Session created already received, ignoring");
|
||||
@@ -236,6 +250,11 @@ class OutboundEstablishState {
|
||||
* @return true if valid
|
||||
*/
|
||||
public synchronized boolean validateSessionCreated() {
|
||||
if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Session created already failed");
|
||||
return false;
|
||||
}
|
||||
if (_receivedSignature != null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Session created already validated");
|
||||
@@ -265,6 +284,9 @@ class OutboundEstablishState {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The SessionCreated validation failed
|
||||
*/
|
||||
public synchronized void fail() {
|
||||
_receivedY = null;
|
||||
_aliceIP = null;
|
||||
@@ -273,10 +295,9 @@ class OutboundEstablishState {
|
||||
_receivedEncryptedSignature = null;
|
||||
_receivedIV = null;
|
||||
_receivedSignature = null;
|
||||
|
||||
if ( (_currentState == OutboundState.OB_STATE_UNKNOWN) ||
|
||||
(_currentState == OutboundState.OB_STATE_CREATED_RECEIVED) )
|
||||
_currentState = OutboundState.OB_STATE_REQUEST_SENT;
|
||||
// sure, there's a chance the packet was corrupted, but in practice
|
||||
// this means that Bob doesn't know his external port, so give up.
|
||||
_currentState = OutboundState.OB_STATE_VALIDATION_FAILED;
|
||||
|
||||
_nextSend = _context.clock().now();
|
||||
}
|
||||
@@ -287,6 +308,8 @@ class OutboundEstablishState {
|
||||
*/
|
||||
private void generateSessionKey() throws DHSessionKeyBuilder.InvalidPublicParameterException {
|
||||
if (_sessionKey != null) return;
|
||||
if (_keyBuilder == null)
|
||||
throw new DHSessionKeyBuilder.InvalidPublicParameterException("Illegal state - never generated a key builder");
|
||||
_keyBuilder.setPeerPublicValue(_receivedY);
|
||||
_sessionKey = _keyBuilder.getSessionKey();
|
||||
ByteArray extra = _keyBuilder.getExtraBytes();
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.i2p.router.transport.udp;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
@@ -659,13 +660,15 @@ class PacketBuilder {
|
||||
_log.debug("Sending request");
|
||||
|
||||
// now for the body
|
||||
System.arraycopy(state.getSentX(), 0, data, off, state.getSentX().length);
|
||||
off += state.getSentX().length;
|
||||
DataHelper.toLong(data, off, 1, state.getSentIP().length);
|
||||
byte[] x = state.getSentX();
|
||||
System.arraycopy(x, 0, data, off, x.length);
|
||||
off += x.length;
|
||||
DataHelper.toLong(data, off, 1, toIP.length);
|
||||
off += 1;
|
||||
System.arraycopy(toIP, 0, data, off, state.getSentIP().length);
|
||||
System.arraycopy(toIP, 0, data, off, toIP.length);
|
||||
off += toIP.length;
|
||||
DataHelper.toLong(data, off, 2, state.getSentPort());
|
||||
int port = state.getSentPort();
|
||||
DataHelper.toLong(data, off, 2, port);
|
||||
off += 2;
|
||||
|
||||
// we can pad here if we want, maybe randomized?
|
||||
@@ -675,7 +678,7 @@ class PacketBuilder {
|
||||
off += 16 - (off % 16);
|
||||
packet.getPacket().setLength(off);
|
||||
authenticate(packet, state.getIntroKey(), state.getIntroKey());
|
||||
setTo(packet, to, state.getSentPort());
|
||||
setTo(packet, to, port);
|
||||
packet.setMessageType(TYPE_SREQ);
|
||||
return packet;
|
||||
}
|
||||
@@ -1050,14 +1053,14 @@ class PacketBuilder {
|
||||
private byte[] getOurExplicitIP() { return null; }
|
||||
private int getOurExplicitPort() { return 0; }
|
||||
|
||||
/** build intro packets for each of the published introducers */
|
||||
@SuppressWarnings("static-access")
|
||||
public UDPPacket[] buildRelayRequest(UDPTransport transport, OutboundEstablishState state, SessionKey ourIntroKey) {
|
||||
/**
|
||||
* build intro packets for each of the published introducers
|
||||
* @return empty list on failure
|
||||
*/
|
||||
public List<UDPPacket> buildRelayRequest(UDPTransport transport, OutboundEstablishState state, SessionKey ourIntroKey) {
|
||||
UDPAddress addr = state.getRemoteAddress();
|
||||
int count = addr.getIntroducerCount();
|
||||
if (count <= 0)
|
||||
return new UDPPacket[0];
|
||||
UDPPacket rv[] = new UDPPacket[count];
|
||||
List<UDPPacket> rv = new ArrayList(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
InetAddress iaddr = addr.getIntroducerHost(i);
|
||||
int iport = addr.getIntroducerPort(i);
|
||||
@@ -1069,8 +1072,9 @@ class PacketBuilder {
|
||||
+ ", as their UDP address is invalid: addr=" + addr + " index=" + i);
|
||||
continue;
|
||||
}
|
||||
// TODO implement some sort of introducer shitlist
|
||||
if (transport.isValid(iaddr.getAddress()))
|
||||
rv[i] = buildRelayRequest(iaddr, iport, ikey, tag, ourIntroKey, state.getIntroNonce(), true);
|
||||
rv.add(buildRelayRequest(iaddr, iport, ikey, tag, ourIntroKey, state.getIntroNonce(), true));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -1227,28 +1231,11 @@ class PacketBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an empty unauthenticated packet for hole punching
|
||||
* Sends an empty unauthenticated packet for hole punching.
|
||||
* Parameters must be validated previously.
|
||||
*/
|
||||
public UDPPacket buildHolePunch(UDPPacketReader reader) {
|
||||
public UDPPacket buildHolePunch(InetAddress to, int port) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
byte data[] = packet.getPacket().getData();
|
||||
Arrays.fill(data, 0, data.length, (byte)0x0);
|
||||
|
||||
int ipSize = reader.getRelayIntroReader().readIPSize();
|
||||
byte ip[] = new byte[ipSize];
|
||||
reader.getRelayIntroReader().readIP(ip, 0);
|
||||
int port = reader.getRelayIntroReader().readPort();
|
||||
|
||||
InetAddress to = null;
|
||||
try {
|
||||
to = InetAddress.getByAddress(ip);
|
||||
} catch (UnknownHostException uhe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("IP for alice to hole punch to is invalid", uhe);
|
||||
packet.release();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending relay hole punch to " + to + ":" + port);
|
||||
|
||||
|
||||
@@ -716,6 +716,14 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
return _peersByIdent.get(remotePeer);
|
||||
}
|
||||
|
||||
/**
|
||||
* For IntroductionManager
|
||||
* @return may be null if not started
|
||||
* @since 0.9.2
|
||||
*/
|
||||
EstablishmentManager getEstablisher() {
|
||||
return _establisher;
|
||||
}
|
||||
/**
|
||||
* Intercept RouterInfo entries received directly from a peer to inject them into
|
||||
* the PeersByCapacity listing.
|
||||
@@ -1682,11 +1690,20 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new DHSessionKeyBuilder
|
||||
* @since 0.9
|
||||
*/
|
||||
DHSessionKeyBuilder getDHBuilder() {
|
||||
return _dhFactory.getBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the factory
|
||||
* @since 0.9.2
|
||||
*/
|
||||
DHSessionKeyBuilder.Factory getDHFactory() {
|
||||
return _dhFactory;
|
||||
}
|
||||
|
||||
private static final int FLAG_ALPHA = 0;
|
||||
private static final int FLAG_IDLE_IN = 1;
|
||||
|
||||
Reference in New Issue
Block a user