SSU2: Refactor in-session PacketBuilder2 methods to throw IOE

Primarily for Peer Test and Relay, so the test or relay can be aborted.
Previously, sends on a dead PeerState2 were not detected or prevented.
Catch IOE throughout where necessary around all build() calls.
Add a fail() method in PeerTestManager
This commit is contained in:
zzz
2022-12-23 15:47:09 -05:00
parent a6f7f9ec12
commit fdbdd10a89
10 changed files with 325 additions and 177 deletions

View File

@@ -1534,9 +1534,12 @@ class EstablishmentManager {
if (_log.shouldDebug())
_log.debug("Found connected introducer " + bob + " for " + state);
long tag = addr.getIntroducerTag(i);
sendRelayRequest(tag, (PeerState2) bob, state);
boolean ok = sendRelayRequest(tag, (PeerState2) bob, state);
// this transitions the state
state2.introSent(h);
if (ok)
state2.introSent(h);
else
state2.setIntroState(h, INTRO_STATE_DISCONNECTED);
return;
}
}
@@ -1637,9 +1640,10 @@ class EstablishmentManager {
* SSU 2 only.
*
* @param charlie must be SSU2
* @return success
* @since 0.9.55
*/
private void sendRelayRequest(long tag, PeerState2 bob, OutboundEstablishState charlie) {
private boolean sendRelayRequest(long tag, PeerState2 bob, OutboundEstablishState charlie) {
// pick our IP based on what address we're connecting to
UDPAddress cra = charlie.getRemoteAddress();
RouterAddress ourra;
@@ -1653,13 +1657,13 @@ class EstablishmentManager {
if (ourra == null) {
if (_log.shouldWarn())
_log.warn("No address to send in relay request");
return;
return false;
}
byte[] ourIP = ourra.getIP();
if (ourIP == null) {
if (_log.shouldWarn())
_log.warn("No IP to send in relay request");
return;
return false;
}
// Bob should already have our RI, especially if we just connected; we do not resend it here.
int ourPort = _transport.getRequestedPort();
@@ -1669,13 +1673,19 @@ class EstablishmentManager {
if (data == null) {
if (_log.shouldWarn())
_log.warn("sig fail");
return;
return false;
}
UDPPacket packet;
try {
packet = _builder2.buildRelayRequest(data, bob);
} catch (IOException ioe) {
return false;
}
UDPPacket packet = _builder2.buildRelayRequest(data, bob);
if (_log.shouldDebug())
_log.debug("Send relay request to " + bob + " for " + charlie);
_transport.send(packet);
bob.setLastSendTime(_context.clock().now());
return true;
}
/**

View File

@@ -192,13 +192,15 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
int reason = rie.getReason();
PeerStateDestroyed psd = createPeerStateDestroyed(reason);
_transport.addRecentlyClosed(psd);
UDPPacket pkt = _transport.getBuilder2().buildSessionDestroyPacket(reason, psd);
_transport.send(pkt);
if (_log.shouldWarn()) {
if (_log.shouldDebug())
_log.debug("Sending TERMINATION reason " + reason + " to " + psd);
_log.warn("IES2 payload error", rie);
}
try {
UDPPacket pkt = _transport.getBuilder2().buildSessionDestroyPacket(reason, psd);
_transport.send(pkt);
if (_log.shouldWarn()) {
if (_log.shouldDebug())
_log.debug("Sending TERMINATION reason " + reason + " to " + psd);
_log.warn("IES2 payload error", rie);
}
} catch (IOException ioe) {}
throw new GeneralSecurityException("IES2 payload error: " + this, rie);
} catch (DataFormatException dfe) {
// no in-session response possible

View File

@@ -1,5 +1,6 @@
package net.i2p.router.transport.udp;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
@@ -527,11 +528,13 @@ class IntroductionManager {
_log.debug("Pinging introducer: " + cur);
cur.setLastPingTime(now);
UDPPacket ping;
if (cur.getVersion() == 2)
ping = _builder2.buildPing((PeerState2) cur);
else
ping = _builder.buildPing(cur);
_transport.send(ping);
try {
if (cur.getVersion() == 2)
ping = _builder2.buildPing((PeerState2) cur);
else
ping = _builder.buildPing(cur);
_transport.send(ping);
} catch (IOException ioe) {}
}
}
}
@@ -892,21 +895,29 @@ class IntroductionManager {
if (gzip)
info = gzipped;
if (info.length <= avail) {
SSU2Payload.RIBlock riblock = new SSU2Payload.RIBlock(info, 0, info.length, false, gzip, 0, 1);
UDPPacket packet = _builder2.buildRelayIntro(idata, riblock, (PeerState2) charlie);
_transport.send(packet);
} else {
DatabaseStoreMessage dbsm = new DatabaseStoreMessage(_context);
dbsm.setEntry(aliceRI);
dbsm.setMessageExpiration(now + 10*1000);
_transport.send(dbsm, charlie);
UDPPacket packet = _builder2.buildRelayIntro(idata, null, (PeerState2) charlie);
// delay because dbsm is queued, we want it to get there first
new DelaySend(packet, 40);
try {
if (info.length <= avail) {
SSU2Payload.RIBlock riblock = new SSU2Payload.RIBlock(info, 0, info.length, false, gzip, 0, 1);
UDPPacket packet = _builder2.buildRelayIntro(idata, riblock, (PeerState2) charlie);
_transport.send(packet);
} else {
DatabaseStoreMessage dbsm = new DatabaseStoreMessage(_context);
dbsm.setEntry(aliceRI);
dbsm.setMessageExpiration(now + 10*1000);
_transport.send(dbsm, charlie);
UDPPacket packet = _builder2.buildRelayIntro(idata, null, (PeerState2) charlie);
// delay because dbsm is queued, we want it to get there first
new DelaySend(packet, 40);
}
charlie.setLastSendTime(now);
return;
} catch (IOException ioe) {
rcode = SSU2Util.RELAY_REJECT_BOB_UNSPEC;
// fall thru to send reject
}
charlie.setLastSendTime(now);
} else {
}
try {
// send rejection to Alice
SigningPrivateKey spk = _context.keyManager().getSigningPrivateKey();
data = SSU2Util.createRelayResponseData(_context, _context.routerHash(), rcode,
@@ -921,7 +932,7 @@ class IntroductionManager {
UDPPacket packet = _builder2.buildRelayResponse(data, alice);
alice.setLastSendTime(now);
_transport.send(packet);
}
} catch (IOException ioe) {}
}
/**
@@ -1129,20 +1140,24 @@ class IntroductionManager {
_log.warn("sig fail");
return;
}
UDPPacket packet = _builder2.buildRelayResponse(data, bob);
if (_log.shouldInfo())
_log.info("Send relay response " + rcode + " as charlie " + " nonce " + nonce + " to bob " + bob +
" with token " + token +
" for alice " + Addresses.toString(testIP, testPort) + ' ' + aliceRI);
_transport.send(packet);
bob.setLastSendTime(now);
try {
UDPPacket packet = _builder2.buildRelayResponse(data, bob);
if (_log.shouldInfo())
_log.info("Send relay response " + rcode + " as charlie " + " nonce " + nonce + " to bob " + bob +
" with token " + token +
" for alice " + Addresses.toString(testIP, testPort) + ' ' + aliceRI);
_transport.send(packet);
bob.setLastSendTime(now);
} catch (IOException ioe) {
return;
}
if (rcode == SSU2Util.RELAY_ACCEPT) {
// send hole punch with the same data we sent to Bob
if (_log.shouldDebug())
_log.debug("Send hole punch to " + Addresses.toString(testIP, testPort));
long sendId = (nonce << 32) | nonce;
long rcvId = ~sendId;
packet = _builder2.buildHolePunch(aliceIP, testPort, aliceIntroKey, sendId, rcvId, data);
UDPPacket packet = _builder2.buildHolePunch(aliceIP, testPort, aliceIntroKey, sendId, rcvId, data);
_transport.send(packet);
}
}
@@ -1205,11 +1220,13 @@ class IntroductionManager {
//idata[0] = 0; // flag
idata[1] = (byte) status;
System.arraycopy(data, 0, idata, 2, data.length);
UDPPacket packet = _builder2.buildRelayResponse(idata, alice);
if (_log.shouldInfo())
_log.info("Got relay response " + status + " as bob, forward " + " nonce " + nonce + " to " + alice);
_transport.send(packet);
alice.setLastSendTime(now);
try {
UDPPacket packet = _builder2.buildRelayResponse(idata, alice);
if (_log.shouldInfo())
_log.info("Got relay response " + status + " as bob, forward " + " nonce " + nonce + " to " + alice);
_transport.send(packet);
alice.setLastSendTime(now);
} catch (IOException ioe) {}
} else {
// We are Alice, give to EstablishmentManager to check sig and process
if (_log.shouldInfo())

View File

@@ -1,5 +1,6 @@
package net.i2p.router.transport.udp;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
@@ -507,10 +508,15 @@ class OutboundMessageFragments {
}
UDPPacket pkt;
if (peer.getVersion() == 1)
if (peer.getVersion() == 1) {
pkt = _builder.buildPacket(sendNext, peer, remaining, newFullAckCount, partialACKBitfields);
else
pkt = _builder2.buildPacket(sendNext, (PeerState2) peer);
} else {
try {
pkt = _builder2.buildPacket(sendNext, (PeerState2) peer);
} catch (IOException ioe) {
pkt = null;
}
}
if (pkt != null) {
if (_log.shouldDebug())
_log.debug("Built packet with " + sendNext.size() + " fragments totalling " + curTotalDataSize +

View File

@@ -1,5 +1,6 @@
package net.i2p.router.transport.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.SocketAddress;
@@ -129,9 +130,9 @@ class PacketBuilder2 {
* this method writes exactly one fragment.
* For no fragments use buildAck().
*
* @return null on error
* @throws IOException if peer is dead
*/
public UDPPacket buildPacket(OutboundMessageState state, int fragment, PeerState2 peer) {
public UDPPacket buildPacket(OutboundMessageState state, int fragment, PeerState2 peer) throws IOException {
List<Fragment> frags = Collections.singletonList(new Fragment(state, fragment));
return buildPacket(frags, peer);
}
@@ -139,8 +140,9 @@ class PacketBuilder2 {
/*
* Multiple fragments
*
* @throws IOException if peer is dead
*/
public UDPPacket buildPacket(List<Fragment> fragments, PeerState2 peer) {
public UDPPacket buildPacket(List<Fragment> fragments, PeerState2 peer) throws IOException {
return buildPacket(fragments, null, peer);
}
@@ -148,8 +150,9 @@ class PacketBuilder2 {
* Multiple fragments and optional other blocks.
*
* @param otherBlocks may be null or empty
* @throws IOException if peer is dead
*/
public UDPPacket buildPacket(List<Fragment> fragments, List<Block> otherBlocks, SSU2Sender peer) {
public UDPPacket buildPacket(List<Fragment> fragments, List<Block> otherBlocks, SSU2Sender peer) throws IOException {
// calculate data size
int numFragments = fragments.size();
int dataSize = 0;
@@ -187,6 +190,7 @@ class PacketBuilder2 {
}
// make the packet
// IOE thrown from here if peer is dead
long pktNum = peer.getNextPacketNumber();
UDPPacket packet = buildShortPacketHeader(peer.getSendConnID(), pktNum, DATA_FLAG_BYTE);
DatagramPacket pkt = packet.getPacket();
@@ -329,8 +333,11 @@ class PacketBuilder2 {
/**
* A DATA packet with padding only.
* We use this for keepalive purposes.
*
* @throws IOException if peer is dead
*/
public UDPPacket buildPing(PeerState2 peer) {
public UDPPacket buildPing(PeerState2 peer) throws IOException {
// IOE thrown from here if peer is dead
long pktNum = peer.getNextPacketNumber();
UDPPacket packet = buildShortPacketHeader(peer.getSendConnID(), pktNum, DATA_FLAG_BYTE);
DatagramPacket pkt = packet.getPacket();
@@ -358,17 +365,19 @@ class PacketBuilder2 {
* An ack packet is just a data packet with no data.
* See buildPacket() for format.
*
*
* @throws IOException if peer is dead
*/
public UDPPacket buildACK(PeerState2 peer) {
public UDPPacket buildACK(PeerState2 peer) throws IOException {
return buildPacket(Collections.<Fragment>emptyList(), peer);
}
/**
* Build a data packet with a termination block.
* This will also include acks, a new token block, and padding.
*
* @throws IOException if peer is dead
*/
public UDPPacket buildSessionDestroyPacket(int reason, SSU2Sender peer) {
public UDPPacket buildSessionDestroyPacket(int reason, SSU2Sender peer) throws IOException {
if (_log.shouldDebug())
_log.debug("Sending termination " + reason + " to : " + peer);
peer.setDestroyReason(reason);
@@ -690,8 +699,9 @@ class PacketBuilder2 {
* In-session, message 1.
*
* @return ready to send packet, non-null
* @throws IOException if peer is dead
*/
public UDPPacket buildPeerTestFromAlice(byte[] signedData, PeerState2 bob) {
public UDPPacket buildPeerTestFromAlice(byte[] signedData, PeerState2 bob) throws IOException {
Block block = new SSU2Payload.PeerTestBlock(1, 0, null, signedData);
UDPPacket rv = buildPacket(Collections.<Fragment>emptyList(), Collections.singletonList(block), bob);
rv.setMessageType(TYPE_TFA);
@@ -726,8 +736,9 @@ class PacketBuilder2 {
*
* @param charlieHash fake hash (all zeros) if rejected by bob
* @return ready to send packet, non-null
* @throws IOException if peer is dead
*/
public UDPPacket buildPeerTestToAlice(int code, Hash charlieHash, byte[] signedData, PeerState2 alice) {
public UDPPacket buildPeerTestToAlice(int code, Hash charlieHash, byte[] signedData, PeerState2 alice) throws IOException {
return buildPeerTestToAlice(code, charlieHash, signedData, null, alice);
}
@@ -739,9 +750,10 @@ class PacketBuilder2 {
* @param charlieHash fake hash (all zeros) if rejected by bob
* @param riBlock to include, may be null
* @return ready to send packet, non-null
* @throws IOException if peer is dead
* @since 0.9.57
*/
public UDPPacket buildPeerTestToAlice(int code, Hash charlieHash, byte[] signedData, Block riBlock, PeerState2 alice) {
public UDPPacket buildPeerTestToAlice(int code, Hash charlieHash, byte[] signedData, Block riBlock, PeerState2 alice) throws IOException {
Block block = new SSU2Payload.PeerTestBlock(4, code, charlieHash, signedData);
List<Block> blocks;
if (riBlock != null) {
@@ -786,8 +798,9 @@ class PacketBuilder2 {
*
* @param riBlock to include, may be null
* @return ready to send packet, non-null
* @throws IOException if peer is dead
*/
public UDPPacket buildPeerTestToCharlie(Hash aliceHash, byte[] signedData, Block riBlock, PeerState2 charlie) {
public UDPPacket buildPeerTestToCharlie(Hash aliceHash, byte[] signedData, Block riBlock, PeerState2 charlie) throws IOException {
Block block = new SSU2Payload.PeerTestBlock(2, 0, aliceHash, signedData);
List<Block> blocks;
if (riBlock != null) {
@@ -808,8 +821,9 @@ class PacketBuilder2 {
* In-session, message 3.
*
* @return ready to send packet, non-null
* @throws IOException if peer is dead
*/
public UDPPacket buildPeerTestToBob(int code, byte[] signedData, PeerState2 bob) {
public UDPPacket buildPeerTestToBob(int code, byte[] signedData, PeerState2 bob) throws IOException {
Block block = new SSU2Payload.PeerTestBlock(3, code, null, signedData);
UDPPacket rv = buildPacket(Collections.<Fragment>emptyList(), Collections.singletonList(block), bob);
rv.setMessageType(TYPE_TCB);
@@ -822,8 +836,9 @@ class PacketBuilder2 {
*
* @param signedData flag + signed data
* @return non-null
* @throws IOException if peer is dead
*/
UDPPacket buildRelayRequest(byte[] signedData, PeerState2 bob) {
UDPPacket buildRelayRequest(byte[] signedData, PeerState2 bob) throws IOException {
Block block = new SSU2Payload.RelayRequestBlock(signedData);
UDPPacket rv = buildPacket(Collections.<Fragment>emptyList(), Collections.singletonList(block), bob);
rv.setMessageType(TYPE_RREQ);
@@ -838,8 +853,9 @@ class PacketBuilder2 {
* @param signedData flag + alice hash + signed data
* @param riBlock to include, may be null
* @return non-null
* @throws IOException if peer is dead
*/
UDPPacket buildRelayIntro(byte[] signedData, Block riBlock, PeerState2 charlie) {
UDPPacket buildRelayIntro(byte[] signedData, Block riBlock, PeerState2 charlie) throws IOException {
Block block = new SSU2Payload.RelayIntroBlock(signedData);
List<Block> blocks;
if (riBlock != null) {
@@ -862,8 +878,9 @@ class PacketBuilder2 {
* @param signedData flag + response code + signed data + optional token
* @param state Alice or Bob
* @return non-null
* @throws IOException if peer is dead
*/
UDPPacket buildRelayResponse(byte[] signedData, PeerState2 state) {
UDPPacket buildRelayResponse(byte[] signedData, PeerState2 state) throws IOException {
Block block = new SSU2Payload.RelayResponseBlock(signedData);
UDPPacket rv = buildPacket(Collections.<Fragment>emptyList(), Collections.singletonList(block), state);
rv.setMessageType(TYPE_RESP);

View File

@@ -1,5 +1,6 @@
package net.i2p.router.transport.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -136,8 +137,10 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
if (isInbound) {
// Send immediate ack of Session Confirmed
_receivedMessages.set(0);
UDPPacket ack = transport.getBuilder2().buildACK(this);
transport.send(ack);
try {
UDPPacket ack = transport.getBuilder2().buildACK(this);
transport.send(ack);
} catch (IOException ioe) {}
} else {
// For outbound, SessionConfirmed is packet 0
_packetNumber.set(1);
@@ -274,8 +277,10 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
// and close the session in super.finishMessages()
if (_log.shouldWarn())
_log.warn("Fail, no Sess Conf ACK rcvd on " + this);
UDPPacket pkt = _transport.getBuilder2().buildSessionDestroyPacket(SSU2Util.REASON_FRAME_TIMEOUT, this);
_transport.send(pkt);
try {
UDPPacket pkt = _transport.getBuilder2().buildSessionDestroyPacket(SSU2Util.REASON_FRAME_TIMEOUT, this);
_transport.send(pkt);
} catch (IOException ioe) {}
_transport.dropPeer(this, true, "No Sess Conf ACK rcvd");
_sessConfForReTX = null;
return false;
@@ -316,7 +321,22 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
* starts at 1 for Alice (0 is Session Confirmed) and 0 for Bob
* @since public since 0.9.57 for SSU2Sender interface only
*/
public long getNextPacketNumber() { return _packetNumber.getAndIncrement(); }
public long getNextPacketNumber() throws IOException {
if (_dead) {
IOException ioe = new IOException("Peer is dead: " + _remotePeer.toBase64());
if (_log.shouldWarn())
_log.warn("Dead: " + this, ioe);
throw ioe;
}
return _packetNumber.getAndIncrement();
}
/**
* For PeerStateDestroyed only, after we are dead
* @since 0.9.57
*/
protected long getNextPacketNumberNoThrow() { return _packetNumber.getAndIncrement(); }
/**
* @since public since 0.9.57 for SSU2Sender interface only
*/
@@ -583,12 +603,14 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
blocks.add(new SSU2Payload.DateTimeBlock(_context));
blocks.add(new SSU2Payload.AddressBlock(toIP.getAddress(), toPort));
blocks.add(new SSU2Payload.PathChallengeBlock(_pathChallengeData));
UDPPacket packet = _transport.getBuilder2().buildPacket(Collections.<Fragment>emptyList(), blocks, this);
// fix up IP/port
DatagramPacket pkt = packet.getPacket();
pkt.setAddress(toIP);
pkt.setPort(toPort);
_transport.send(packet);
try {
UDPPacket packet = _transport.getBuilder2().buildPacket(Collections.<Fragment>emptyList(), blocks, this);
// fix up IP/port
DatagramPacket pkt = packet.getPacket();
pkt.setAddress(toIP);
pkt.setPort(toPort);
_transport.send(packet);
} catch (IOException ioe) {}
}
/////////////////////////////////////////////////////////
@@ -650,10 +672,12 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
}
if (tag > 0) {
SSU2Payload.Block block = new SSU2Payload.RelayTagBlock(tag);
UDPPacket pkt = _transport.getBuilder2().buildPacket(Collections.<Fragment>emptyList(),
Collections.singletonList(block),
this);
_transport.send(pkt);
try {
UDPPacket pkt = _transport.getBuilder2().buildPacket(Collections.<Fragment>emptyList(),
Collections.singletonList(block),
this);
_transport.send(pkt);
} catch (IOException ioe) {}
}
}
@@ -826,8 +850,10 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
if (reason == SSU2Util.REASON_TERMINATION) {
// this should only happen at shutdown, where we don't have a post-termination handler
} else {
UDPPacket pkt = _transport.getBuilder2().buildSessionDestroyPacket(SSU2Util.REASON_TERMINATION, this);
_transport.send(pkt);
try {
UDPPacket pkt = _transport.getBuilder2().buildSessionDestroyPacket(SSU2Util.REASON_TERMINATION, this);
_transport.send(pkt);
} catch (IOException ioe) {}
}
if (!_dead) {
_transport.getEstablisher().receiveSessionDestroy(_remoteHostId, this);
@@ -839,14 +865,16 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
if (_log.shouldInfo())
_log.info("Got PATH CHALLENGE block, length: " + data.length + " on " + this);
SSU2Payload.Block block = new SSU2Payload.PathResponseBlock(data);
UDPPacket pkt = _transport.getBuilder2().buildPacket(Collections.<Fragment>emptyList(),
Collections.singletonList(block),
this);
// TODO send to from address?
_transport.send(pkt);
long now = _context.clock().now();
setLastSendTime(now);
setLastReceiveTime(now);
try {
UDPPacket pkt = _transport.getBuilder2().buildPacket(Collections.<Fragment>emptyList(),
Collections.singletonList(block),
this);
// TODO send to from address?
_transport.send(pkt);
long now = _context.clock().now();
setLastSendTime(now);
setLastReceiveTime(now);
} catch (IOException ioe) {}
}
public void gotPathResponse(RemoteHostId from, byte[] data) {
@@ -866,13 +894,15 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
if (isIPv6() || !_transport.isSnatted()) {
EstablishmentManager.Token token = _transport.getEstablisher().getInboundToken(from);
SSU2Payload.Block block = new SSU2Payload.NewTokenBlock(token);
UDPPacket pkt = _transport.getBuilder2().buildPacket(Collections.<Fragment>emptyList(),
Collections.singletonList(block),
this);
_transport.send(pkt);
long now = _context.clock().now();
setLastSendTime(now);
setLastReceiveTime(now);
try {
UDPPacket pkt = _transport.getBuilder2().buildPacket(Collections.<Fragment>emptyList(),
Collections.singletonList(block),
this);
_transport.send(pkt);
long now = _context.clock().now();
setLastSendTime(now);
setLastReceiveTime(now);
} catch (IOException ioe) {}
} else {
messagePartiallyReceived();
}
@@ -1109,10 +1139,12 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
}
_wantACKSendSince = 0;
}
UDPPacket ack = _transport.getBuilder2().buildACK(PeerState2.this);
if (_log.shouldDebug())
_log.debug("ACKTimer sending acks to " + PeerState2.this);
_transport.send(ack);
try {
UDPPacket ack = _transport.getBuilder2().buildACK(PeerState2.this);
if (_log.shouldDebug())
_log.debug("ACKTimer sending acks to " + PeerState2.this);
_transport.send(ack);
} catch (IOException ioe) {}
}
}
}

View File

@@ -1,5 +1,6 @@
package net.i2p.router.transport.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -73,7 +74,7 @@ class PeerStateDestroyed implements SSU2Payload.PayloadCallback, SSU2Sender {
_log = ctx.logManager().getLog(PeerStateDestroyed.class);
_remoteHostId = peer.getRemoteHostId();
_mtu = peer.getMTU();
_packetNumber = new AtomicInteger((int) peer.getNextPacketNumber());
_packetNumber = new AtomicInteger((int) peer.getNextPacketNumberNoThrow());
_sendConnID = peer.getSendConnID();
_rcvConnID = peer.getRcvConnID();
_sendCha = peer.getSendCipher();
@@ -370,10 +371,12 @@ class PeerStateDestroyed implements SSU2Payload.PayloadCallback, SSU2Sender {
// If we received a destroy, send reason_termination
// otherwise, send the original reason.
UDPPacket pkt = _transport.getBuilder2().buildSessionDestroyPacket(_destroyReason, PeerStateDestroyed.this);
if (_log.shouldDebug())
_log.debug("Sending TERMINATION reason " + _destroyReason + " to " + PeerStateDestroyed.this);
_transport.send(pkt);
try {
UDPPacket pkt = _transport.getBuilder2().buildSessionDestroyPacket(_destroyReason, PeerStateDestroyed.this);
if (_log.shouldDebug())
_log.debug("Sending TERMINATION reason " + _destroyReason + " to " + PeerStateDestroyed.this);
_transport.send(pkt);
} catch (IOException ioe) {}
if (_destroyReason != REASON_TERMINATION) {
_delay *= 2;
reschedule(_delay);

View File

@@ -1,5 +1,6 @@
package net.i2p.router.transport.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.Inet6Address;
@@ -380,7 +381,12 @@ class PeerTestManager {
}
test.setTestData(data);
}
packet = _packetBuilder2.buildPeerTestFromAlice(data, bob2);
try {
packet = _packetBuilder2.buildPeerTestFromAlice(data, bob2);
} catch (IOException ioe) {
fail();
return;
}
}
_transport.send(packet);
long now = _context.clock().now();
@@ -607,7 +613,25 @@ class PeerTestManager {
}
}
}
/**
* Reset all state and call testComplete(). We are Alice.
*
* call from a synchronized method
*
* @since 0.9.57
*/
private void fail() {
// so testComplete() will return UNKNOWN
PeerTestState test = _currentTest;
if (test == null)
return;
test.setAlicePortFromCharlie(0);
test.setReceiveCharlieTime(0);
test.setReceiveBobTime(0);
testComplete();
}
/**
* Evaluate the info we have and act accordingly, since the test has either timed out or
* we have successfully received the second PeerTest from a Charlie.
@@ -1081,21 +1105,29 @@ class PeerTestManager {
_log.debug("Retx msg 4 to alice on " + state);
// we already sent to alice, send it again
PeerState2 alice = state.getAlice();
UDPPacket packet = _packetBuilder2.buildPeerTestToAlice(state.getStatus(), state.getCharlieHash(), data, alice);
_transport.send(packet);
alice.setLastSendTime(now);
state.setSendAliceTime(now);
state.setReceiveAliceTime(now);
try {
UDPPacket packet = _packetBuilder2.buildPeerTestToAlice(state.getStatus(), state.getCharlieHash(), data, alice);
_transport.send(packet);
alice.setLastSendTime(now);
state.setSendAliceTime(now);
state.setReceiveAliceTime(now);
} catch (IOException ioe) {
_activeTests.remove(lNonce);
}
return;
} else if (msg == 2) {
if (_log.shouldDebug())
_log.debug("Retx msg 3 to bob on " + state);
PeerState2 bob = (PeerState2) state.getBob();
UDPPacket packet = _packetBuilder2.buildPeerTestToBob(state.getStatus(), data, bob);
_transport.send(packet);
bob.setLastSendTime(now);
state.setReceiveBobTime(now);
// should we retx msg 5 also?
try {
UDPPacket packet = _packetBuilder2.buildPeerTestToBob(state.getStatus(), data, bob);
_transport.send(packet);
bob.setLastSendTime(now);
state.setReceiveBobTime(now);
// should we retx msg 5 also?
} catch (IOException ioe) {
_activeTests.remove(lNonce);
}
return;
} else {
// msg 1 but haven't heard from a good charlie yet
@@ -1114,13 +1146,15 @@ class PeerTestManager {
if (_log.shouldWarn())
_log.warn("Too many active tests, droppping from " + Addresses.toString(fromIP, fromPort));
UDPPacket packet;
if (msg == 1)
packet = _packetBuilder2.buildPeerTestToAlice(SSU2Util.TEST_REJECT_BOB_LIMIT,
Hash.FAKE_HASH, data, fromPeer);
else
packet = _packetBuilder2.buildPeerTestToBob(SSU2Util.TEST_REJECT_CHARLIE_LIMIT,
data, fromPeer);
_transport.send(packet);
try {
if (msg == 1)
packet = _packetBuilder2.buildPeerTestToAlice(SSU2Util.TEST_REJECT_BOB_LIMIT,
Hash.FAKE_HASH, data, fromPeer);
else
packet = _packetBuilder2.buildPeerTestToBob(SSU2Util.TEST_REJECT_CHARLIE_LIMIT,
data, fromPeer);
_transport.send(packet);
} catch (IOException ioe) {}
return;
}
} else {
@@ -1157,18 +1191,14 @@ class PeerTestManager {
!DataHelper.eq(fromPeer.getRemoteIP(), 0, testIP, 0, isIPv6 ? 8 : 4)) {
if (_log.shouldWarn())
_log.warn("Invalid PeerTest address: " + Addresses.toString(testIP, testPort));
UDPPacket packet = _packetBuilder2.buildPeerTestToAlice(SSU2Util.TEST_REJECT_BOB_ADDRESS,
Hash.FAKE_HASH, data, fromPeer);
_transport.send(packet);
sendRejectToAlice(SSU2Util.TEST_REJECT_BOB_ADDRESS, data, fromPeer);
fromPeer.setLastSendTime(now);
return;
}
if (_throttle.shouldThrottle(fromIP)) {
if (_log.shouldLog(Log.WARN))
_log.warn("PeerTest throttle from " + Addresses.toString(fromIP, fromPort));
UDPPacket packet = _packetBuilder2.buildPeerTestToAlice(SSU2Util.TEST_REJECT_BOB_LIMIT,
Hash.FAKE_HASH, data, fromPeer);
_transport.send(packet);
sendRejectToAlice(SSU2Util.TEST_REJECT_BOB_LIMIT, data, fromPeer);
fromPeer.setLastSendTime(now);
return;
}
@@ -1178,9 +1208,7 @@ class PeerTestManager {
if (_log.shouldLog(Log.WARN))
_log.warn("No alice RI");
// send reject
UDPPacket packet = _packetBuilder2.buildPeerTestToAlice(SSU2Util.TEST_REJECT_BOB_UNSPEC,
Hash.FAKE_HASH, data, fromPeer);
_transport.send(packet);
sendRejectToAlice(SSU2Util.TEST_REJECT_BOB_UNSPEC, data, fromPeer);
fromPeer.setLastSendTime(now);
return;
}
@@ -1192,9 +1220,7 @@ class PeerTestManager {
if (_log.shouldWarn())
_log.warn("Signature failed msg 1\n" + aliceRI);
// send reject
UDPPacket packet = _packetBuilder2.buildPeerTestToAlice(SSU2Util.TEST_REJECT_BOB_SIGFAIL,
Hash.FAKE_HASH, data, fromPeer);
_transport.send(packet);
sendRejectToAlice(SSU2Util.TEST_REJECT_BOB_SIGFAIL, data, fromPeer);
fromPeer.setLastSendTime(now);
return;
}
@@ -1203,9 +1229,7 @@ class PeerTestManager {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to pick a charlie (no peer), IPv6? " + isIPv6);
// send reject
UDPPacket packet = _packetBuilder2.buildPeerTestToAlice(SSU2Util.TEST_REJECT_BOB_NO_CHARLIE,
Hash.FAKE_HASH, data, fromPeer);
_transport.send(packet);
sendRejectToAlice(SSU2Util.TEST_REJECT_BOB_NO_CHARLIE, data, fromPeer);
fromPeer.setLastSendTime(now);
return;
}
@@ -1229,7 +1253,12 @@ class PeerTestManager {
if (_log.shouldDebug())
_log.debug("Send Alice RI and msg 2 to charlie on " + state);
// forward to charlie, don't bother to validate signed data
sendRIandPT(aliceRI, -1, alice, data, (PeerState2) charlie, now);
try {
sendRIandPT(aliceRI, -1, alice, data, (PeerState2) charlie, now);
} catch (IOException ioe) {
sendRejectToAlice(SSU2Util.TEST_REJECT_BOB_UNSPEC, data, fromPeer);
_activeTests.remove(lNonce);
}
break;
}
@@ -1311,15 +1340,21 @@ class PeerTestManager {
if (data == null) {
if (_log.shouldWarn())
_log.warn("sig fail");
if (rcode == SSU2Util.TEST_ACCEPT)
_activeTests.remove(lNonce);
return;
if (rcode == SSU2Util.TEST_ACCEPT)
_activeTests.remove(lNonce);
return;
}
try {
UDPPacket packet = _packetBuilder2.buildPeerTestToBob(rcode, data, fromPeer);
if (_log.shouldDebug())
_log.debug("Send msg 3 response " + rcode + " nonce " + lNonce + " to " + fromPeer);
_transport.send(packet);
fromPeer.setLastSendTime(now);
} catch (IOException ioe) {
if (rcode == SSU2Util.TEST_ACCEPT)
_activeTests.remove(lNonce);
return;
}
UDPPacket packet = _packetBuilder2.buildPeerTestToBob(rcode, data, fromPeer);
if (_log.shouldDebug())
_log.debug("Send msg 3 response " + rcode + " nonce " + lNonce + " to " + fromPeer);
_transport.send(packet);
fromPeer.setLastSendTime(now);
if (rcode == SSU2Util.TEST_ACCEPT) {
// send msg 5
if (_log.shouldDebug())
@@ -1327,7 +1362,7 @@ class PeerTestManager {
long sendId = (nonce << 32) | nonce;
long rcvId = ~sendId;
// send the same data we sent to Bob
packet = _packetBuilder2.buildPeerTestToAlice(aliceIP, testPort,
UDPPacket packet = _packetBuilder2.buildPeerTestToAlice(aliceIP, testPort,
aliceIntroKey, true,
sendId, rcvId, data);
_transport.send(packet);
@@ -1351,12 +1386,16 @@ class PeerTestManager {
Hash alice = state.getAlice().getRemotePeer();
RouterInfo aliceRI = _context.netDb().lookupRouterInfoLocally(alice);
if (aliceRI != null) {
if (_log.shouldInfo())
_log.info("Charlie response " + status + " picked a new one " + charlie + " on " + state);
state.setCharlie(charlie.getRemoteIPAddress(), charlie.getRemotePort(), charlie.getRemotePeer());
state.setLastSendTime(now);
sendRIandPT(aliceRI, -1, alice, state.getTestData(), (PeerState2) charlie, now);
break;
try {
state.setCharlie(charlie.getRemoteIPAddress(), charlie.getRemotePort(), charlie.getRemotePeer());
state.setLastSendTime(now);
sendRIandPT(aliceRI, -1, alice, state.getTestData(), (PeerState2) charlie, now);
if (_log.shouldInfo())
_log.info("Charlie response " + status + " picked a new one " + charlie + " on " + state);
break;
} catch (IOException ioe) {
// give up
}
}
}
}
@@ -1391,13 +1430,16 @@ class PeerTestManager {
// FIXME this will probably get there before the RI
if (_log.shouldDebug())
_log.debug("Send msg 4 status " + status + " to alice on " + state);
sendRIandPT(charlieRI, status, charlie, data, alice, now);
// overwrite alice-signed test data with charlie-signed data in case we need to retransmit
state.setStatus(status);
state.setSendAliceTime(now);
state.setTestData(data);
// we should be done, but stick around for possible retx to alice
//_activeTests.remove(lNonce);
try {
sendRIandPT(charlieRI, status, charlie, data, alice, now);
// overwrite alice-signed test data with charlie-signed data in case we need to retransmit
state.setStatus(status);
state.setSendAliceTime(now);
state.setTestData(data);
// we should be done, but stick around for possible retx to alice
} catch (IOException ioe) {
_activeTests.remove(lNonce);
}
break;
}
@@ -1487,12 +1529,7 @@ class PeerTestManager {
}
}
if (charlieIntroKey == null || charlieIP == null || charliePort <= 0) {
// reset all state
// so testComplete() will return UNKNOWN
test.setAlicePortFromCharlie(0);
test.setReceiveCharlieTime(0);
test.setReceiveBobTime(0);
testComplete();
fail();
return;
}
InetAddress oldIP = test.getCharlieIP();
@@ -1769,6 +1806,18 @@ class PeerTestManager {
}
}
/**
* Send reject to Alice. We are Bob. SSU2 only.
*
* @since 0.9.57
*/
private void sendRejectToAlice(int reason, byte[] data, PeerState2 alice) {
try {
UDPPacket packet = _packetBuilder2.buildPeerTestToAlice(reason, Hash.FAKE_HASH, data, alice);
_transport.send(packet);
} catch (IOException ioe) {}
}
/**
* Get an address out of a RI. SSU2 only.
*
@@ -2205,8 +2254,9 @@ class PeerTestManager {
* @param data signed peer test data
* @param to charlie for msg 2, alice for msg 4
* @since 0.9.57
* @throws IOException if to peer is dead
*/
private void sendRIandPT(RouterInfo ri, int status, Hash hash, byte[] data, PeerState2 to, long now) {
private void sendRIandPT(RouterInfo ri, int status, Hash hash, byte[] data, PeerState2 to, long now) throws IOException {
boolean delay = false;
SSU2Payload.RIBlock riblock = null;
if (ri != null) {

View File

@@ -1,5 +1,6 @@
package net.i2p.router.transport.udp;
import java.io.IOException;
import java.net.InetAddress;
import java.util.List;
@@ -17,7 +18,7 @@ interface SSU2Sender {
InetAddress getRemoteIPAddress();
int getRemotePort();
int getMTU();
long getNextPacketNumber();
long getNextPacketNumber() throws IOException;
long getSendConnID();
CipherState getSendCipher();
byte[] getSendHeaderEncryptKey1();

View File

@@ -1934,9 +1934,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if (_log.shouldLog(Log.WARN))
_log.warn("Peer already connected (PBRH): old=" + oldPeer2 + " new=" + peer);
// transfer over the old state/inbound message fragments/etc
// Send destroy before loadFrom(), because loadFrom() sets dead = true
sendDestroy(oldPeer2, SSU2Util.REASON_REPLACED);
peer.loadFrom(oldPeer2);
oldEstablishedOn = oldPeer2.getKeyEstablishedTime();
sendDestroy(oldPeer2, SSU2Util.REASON_REPLACED);
oldPeer2.dropOutbound();
_introManager.remove(oldPeer2);
}
@@ -2351,7 +2352,11 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
return;
pkt = _packetBuilder.buildSessionDestroyPacket(peer);
} else {
pkt = _packetBuilder2.buildSessionDestroyPacket(reasonCode, (PeerState2) peer);
try {
pkt = _packetBuilder2.buildSessionDestroyPacket(reasonCode, (PeerState2) peer);
} catch (IOException ioe) {
return;
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending destroy to : " + peer);
@@ -3826,10 +3831,15 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// or else session will stay open forever?
//peer.setLastSendTime(now);
UDPPacket ping;
if (peer.getVersion() == 2)
ping = _packetBuilder2.buildPing((PeerState2) peer);
else
if (peer.getVersion() == 2) {
try {
ping = _packetBuilder2.buildPing((PeerState2) peer);
} catch (IOException ioe) {
continue;
}
} else {
ping = _packetBuilder.buildPing(peer);
}
send(ping);
peer.setLastPingTime(now);
// If external port is different, it may be changing the port for every