SSU2: Persist SSU2 tokens

Move establisher and packet handler initialization
to after the address initialization
This commit is contained in:
zzz
2022-07-08 14:12:31 -04:00
parent 70596d0548
commit 2a4ab118de
2 changed files with 169 additions and 3 deletions

View File

@ -1,5 +1,13 @@
package net.i2p.router.transport.udp;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.UnknownHostException;
@ -42,6 +50,7 @@ import net.i2p.util.HexDump;
import net.i2p.util.I2PThread;
import net.i2p.util.LHMCache;
import net.i2p.util.Log;
import net.i2p.util.SecureFileOutputStream;
import net.i2p.util.SystemVersion;
import net.i2p.util.VersionComparator;
@ -161,6 +170,7 @@ class EstablishmentManager {
private static final int MAX_TOKENS = 512;
public static final long IB_TOKEN_EXPIRATION = 60*60*1000L;
private static final long MAX_SKEW = 2*60*1000;
private static final String TOKEN_FILE = "ssu2tokens.txt";
public EstablishmentManager(RouterContext ctx, UDPTransport transport) {
@ -216,6 +226,8 @@ class EstablishmentManager {
}
public synchronized void startup() {
if (_enableSSU2)
loadTokens();
_alive = true;
I2PThread t = new I2PThread(new Establisher(), "UDP Establisher", true);
t.start();
@ -223,6 +235,8 @@ class EstablishmentManager {
public synchronized void shutdown() {
_alive = false;
if (_enableSSU2)
saveTokens();
notifyActivity();
}
@ -2482,6 +2496,8 @@ class EstablishmentManager {
public void ipChanged(boolean isIPv6) {
if (!_enableSSU2)
return;
if (_log.shouldWarn())
_log.warn("IP changed, ipv6? " + isIPv6);
int len = isIPv6 ? 16 : 4;
// expire while we're at it
long now = _context.clock().now();
@ -2574,6 +2590,156 @@ class EstablishmentManager {
}
}
/**
* Format:
*
*<pre>
* 4 ourIPv4addr ourIPv4port
* 6 ourIPv6addr ourIPv6port
* I addr port token exp
* O addr port token exp
*</pre>
*
* @since 0.9.55
*/
private void loadTokens() {
File f = new File(_context.getConfigDir(), TOKEN_FILE);
String ourV4Port = Integer.toString(_transport.getExternalPort(false));
String ourV6Port = Integer.toString(_transport.getExternalPort(true));
String ourV4Addr;
RouterAddress addr = _transport.getCurrentAddress(false);
if (addr != null)
ourV4Addr = addr.getHost();
else
ourV4Addr = null;
String ourV6Addr;
addr = _transport.getCurrentAddress(true);
if (addr != null)
ourV6Addr = addr.getHost();
else
ourV6Addr = null;
if (_log.shouldDebug())
_log.debug("Loading SSU2 tokens for " + ourV4Addr + ' ' + ourV4Port + ' ' + ourV6Addr + ' ' + ourV6Port);
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(f));
boolean v4Match = false;
boolean v6Match = false;
long now = _context.clock().now();
int count = 0;
synchronized(_inboundTokens) {
synchronized(_outboundTokens) {
String line;
while ((line = DataHelper.readLine(in)) != null) {
if (line.startsWith("#"))
continue;
String[] s = DataHelper.split(line, " ", 5);
if (s.length < 3)
continue;
if (s[0].equals("4")) {
v4Match = s[1].equals(ourV4Addr) && s[2].trim().equals(ourV4Port);
//if (_log.shouldWarn())
// _log.warn("V4 match? " + v4Match + ' ' + line);
} else if (s[0].equals("6")) {
v6Match = s[1].equals(ourV6Addr) && s[2].trim().equals(ourV6Port);
//if (_log.shouldWarn())
// _log.warn("V6 match? " + v6Match + ' ' + line);
} else if ((s[0].equals("I") || s[0].equals("O")) && s.length == 5) {
boolean isV6 = s[1].contains(":");
if ((isV6 && !v6Match) || (!isV6 && !v4Match))
continue;
try {
long exp = Long.parseLong(s[4].trim());
if (exp > now) {
byte[] ip = Addresses.getIPOnly(s[1]);
if (ip != null) {
int port = Integer.parseInt(s[2]);
long tok = Long.parseLong(s[3]);
RemoteHostId id = new RemoteHostId(ip, port);
Token token = new Token(tok, exp);
if (s[0].equals("I"))
_inboundTokens.put(id, token);
else
_outboundTokens.put(id, token);
count++;
}
}
} catch (NumberFormatException nfe) {}
}
}
}
}
if (_log.shouldDebug())
_log.debug("Loaded " + count + " SSU2 tokens");
} catch (IOException ioe) {
if (_log.shouldWarn())
_log.warn("Failed to load SSU2 tokens", ioe);
} finally {
if (in != null) {
try { in.close(); } catch (IOException ioe) {}
f.delete();
}
}
}
/**
* @since 0.9.55
*/
private void saveTokens() {
File f = new File(_context.getConfigDir(), TOKEN_FILE);
PrintWriter out = null;
try {
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(f), "ISO-8859-1")));
out.println("# SSU2 tokens, format: IPv4/IPv6/In/Out addr port token expiration");
RouterAddress addr = _transport.getCurrentAddress(false);
if (addr != null) {
String us = addr.getHost();
if (us != null)
out.println("4 " + us + ' ' + _transport.getExternalPort(false));
}
addr = _transport.getCurrentAddress(true);
if (addr != null) {
String us = addr.getHost();
if (us != null)
out.println("6 " + us + ' ' + _transport.getExternalPort(true));
}
long now = _context.clock().now();
int count = 0;
synchronized(_inboundTokens) {
for (Map.Entry<RemoteHostId, Token> e : _inboundTokens.entrySet()) {
Token token = e.getValue();
long exp = token.expires;
if (exp <= now)
continue;
RemoteHostId id = e.getKey();
out.println("I " + Addresses.toString(id.getIP()) + ' ' + id.getPort() + ' ' + token.token + ' ' + exp);
count++;
}
}
synchronized(_outboundTokens) {
for (Map.Entry<RemoteHostId, Token> e : _outboundTokens.entrySet()) {
Token token = e.getValue();
long exp = token.expires;
if (exp <= now)
continue;
RemoteHostId id = e.getKey();
out.println("O " + Addresses.toString(id.getIP()) + ' ' + id.getPort() + ' ' + token.token + ' ' + exp);
count++;
}
}
if (out.checkError())
throw new IOException("Failed write to " + f);
if (_log.shouldDebug())
_log.debug("Stored " + count + " SSU2 tokens to " + f);
} catch (IOException ioe) {
if (_log.shouldWarn())
_log.warn("Error writing the SSU2 tokens file", ioe);
} finally {
if (out != null) out.close();
}
}
/**
* Process SSU2 hole punch payload
*

View File

@ -717,13 +717,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_context.router().saveConfig(changes, null);
}
_handler.startup();
_fragments.startup();
_inboundFragments.startup();
_pusher = new PacketPusher(_context, _fragments, _endpoints);
_pusher.startup();
// must be after pusher
_establisher.startup();
if (USE_PRIORITY)
_refiller.startup();
//if (SHOULD_FLOOD_PEERS)
@ -820,6 +817,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
else
setReachabilityStatus(Status.REJECT_UNSOLICITED);
}
// must be after pusher and setting external addresses
_establisher.startup();
_handler.startup();
rebuildExternalAddress(false, false);
}