propagate from branch 'i2p.i2p.zzz.test2' (head 6ccd9ca652057494bb2857e87636f18aadcd33f3)
to branch 'i2p.i2p' (head 376f751adc13923cdbf4f659c3f23ca957cf47b3)
@ -182,7 +182,7 @@ Applications:
|
||||
By welterde.
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
Jetty 8.1.15.v20140411:
|
||||
Jetty 8.1.16.v20140903:
|
||||
See licenses/ABOUT-Jetty.html
|
||||
See licenses/NOTICE-Jetty.html
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
|
@ -64,10 +64,11 @@ class ConfigParser {
|
||||
if (inputLine.startsWith(";")) {
|
||||
return "";
|
||||
}
|
||||
if (inputLine.split("#").length > 0) {
|
||||
return inputLine.split("#")[0];
|
||||
int hash = inputLine.indexOf('#');
|
||||
if (hash >= 0) {
|
||||
return inputLine.substring(0, hash);
|
||||
} else {
|
||||
return "";
|
||||
return inputLine;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,15 +100,15 @@
|
||||
<target name="war" depends="jar, bundle, warUpToDate, listChangedFiles" unless="war.uptodate" >
|
||||
<!-- set if unset -->
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<copy todir="build/icons/.icons" >
|
||||
<fileset dir="../icons/" />
|
||||
<copy todir="build/resources/.resources" >
|
||||
<fileset dir="../resources/" />
|
||||
</copy>
|
||||
<!-- mime.properties must be in with the classes -->
|
||||
<copy file="../mime.properties" todir="build/obj/org/klomp/snark/web" />
|
||||
<war destfile="../i2psnark.war" webxml="../web.xml" >
|
||||
<!-- include only the web stuff, as of 0.7.12 the router will add i2psnark.jar to the classpath for the war -->
|
||||
<classes dir="./build/obj" includes="**/web/*" />
|
||||
<fileset dir="build/icons/" />
|
||||
<fileset dir="build/resources/" />
|
||||
<manifest>
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
@ -121,7 +121,7 @@
|
||||
|
||||
<target name="warUpToDate">
|
||||
<uptodate property="war.uptodate" targetfile="../i2psnark.war" >
|
||||
<srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../icons/* ../web.xml" />
|
||||
<srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../resources/**/* ../web.xml" />
|
||||
</uptodate>
|
||||
</target>
|
||||
|
||||
|
@ -57,8 +57,8 @@ public class Peer implements Comparable<Peer>
|
||||
private DataOutputStream dout;
|
||||
|
||||
/** running counters */
|
||||
private long downloaded;
|
||||
private long uploaded;
|
||||
private final AtomicLong downloaded = new AtomicLong();
|
||||
private final AtomicLong uploaded = new AtomicLong();
|
||||
|
||||
// Keeps state for in/out connections. Non-null when the handshake
|
||||
// was successful, the connection setup and runs
|
||||
@ -618,7 +618,7 @@ public class Peer implements Comparable<Peer>
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void downloaded(int size) {
|
||||
downloaded += size;
|
||||
downloaded.addAndGet(size);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -626,7 +626,7 @@ public class Peer implements Comparable<Peer>
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void uploaded(int size) {
|
||||
uploaded += size;
|
||||
uploaded.addAndGet(size);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -635,7 +635,7 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
public long getDownloaded()
|
||||
{
|
||||
return downloaded;
|
||||
return downloaded.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -644,7 +644,7 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
public long getUploaded()
|
||||
{
|
||||
return uploaded;
|
||||
return uploaded.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -652,8 +652,8 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
public void resetCounters()
|
||||
{
|
||||
downloaded = 0;
|
||||
uploaded = 0;
|
||||
downloaded.set(0);
|
||||
uploaded.set(0);
|
||||
}
|
||||
|
||||
public long getInactiveTime() {
|
||||
|
@ -27,7 +27,6 @@ import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@ -245,16 +244,19 @@ public class Snark
|
||||
*
|
||||
* @deprecated unused
|
||||
*/
|
||||
/****
|
||||
Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
|
||||
StorageListener slistener, CoordinatorListener clistener) {
|
||||
this(util, torrent, ip, user_port, slistener, clistener, null, null, null, true, ".");
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* single torrent - via router
|
||||
*
|
||||
* @deprecated unused
|
||||
*/
|
||||
/****
|
||||
public Snark(I2PAppContext ctx, Properties opts, String torrent,
|
||||
StorageListener slistener, boolean start, String rootDir) {
|
||||
this(new I2PSnarkUtil(ctx), torrent, null, -1, slistener, null, null, null, null, false, rootDir);
|
||||
@ -284,6 +286,7 @@ public class Snark
|
||||
if (start)
|
||||
this.startTorrent();
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* multitorrent
|
||||
@ -515,18 +518,13 @@ public class Snark
|
||||
|
||||
// Create a new ID and fill it with something random. First nine
|
||||
// zeros bytes, then three bytes filled with snark and then
|
||||
// sixteen random bytes.
|
||||
// eight random bytes.
|
||||
byte snark = (((3 + 7 + 10) * (1000 - 8)) / 992) - 17;
|
||||
byte[] rv = new byte[20];
|
||||
Random random = I2PAppContext.getGlobalContext().random();
|
||||
int i;
|
||||
for (i = 0; i < 9; i++)
|
||||
rv[i] = 0;
|
||||
rv[i++] = snark;
|
||||
rv[i++] = snark;
|
||||
rv[i++] = snark;
|
||||
while (i < 20)
|
||||
rv[i++] = (byte)random.nextInt(256);
|
||||
rv[9] = snark;
|
||||
rv[10] = snark;
|
||||
rv[11] = snark;
|
||||
I2PAppContext.getGlobalContext().random().nextBytes(rv, 12, 8);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -958,6 +956,7 @@ public class Snark
|
||||
* non-valid argument list. The given listeners will be
|
||||
* passed to all components that take one.
|
||||
*/
|
||||
/****
|
||||
private static Snark parseArguments(String[] args,
|
||||
StorageListener slistener,
|
||||
CoordinatorListener clistener)
|
||||
@ -972,6 +971,7 @@ public class Snark
|
||||
int i = 0;
|
||||
while (i < args.length)
|
||||
{
|
||||
****/
|
||||
/*
|
||||
if (args[i].equals("--debug"))
|
||||
{
|
||||
@ -993,7 +993,9 @@ public class Snark
|
||||
catch (NumberFormatException nfe) { }
|
||||
}
|
||||
}
|
||||
else */ if (args[i].equals("--port"))
|
||||
else */
|
||||
/****
|
||||
if (args[i].equals("--port"))
|
||||
{
|
||||
if (args.length - 1 < i + 1)
|
||||
usage("--port needs port number to listen on");
|
||||
@ -1099,6 +1101,7 @@ public class Snark
|
||||
System.out.println
|
||||
(" \tor (with --share) a file to share.");
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* Aborts program abnormally.
|
||||
|
@ -600,10 +600,10 @@ public class SnarkManager implements CompleteListener {
|
||||
|
||||
/**
|
||||
* Get all themes
|
||||
* @return String[] -- Array of all the themes found.
|
||||
* @return String[] -- Array of all the themes found, non-null, unsorted
|
||||
*/
|
||||
public String[] getThemes() {
|
||||
String[] themes = null;
|
||||
String[] themes;
|
||||
// "docs/themes/snark/"
|
||||
File dir = new File(_context.getBaseDir(), "docs/themes/snark");
|
||||
FileFilter fileFilter = new FileFilter() { public boolean accept(File file) { return file.isDirectory(); } };
|
||||
@ -614,6 +614,8 @@ public class SnarkManager implements CompleteListener {
|
||||
for(int i = 0; i < dirnames.length; i++) {
|
||||
themes[i] = dirnames[i].getName();
|
||||
}
|
||||
} else {
|
||||
themes = new String[0];
|
||||
}
|
||||
// return the map.
|
||||
return themes;
|
||||
|
531
apps/i2psnark/java/src/org/klomp/snark/web/Sorters.java
Normal file
@ -0,0 +1,531 @@
|
||||
package org.klomp.snark.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.text.Collator;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.klomp.snark.MetaInfo;
|
||||
import org.klomp.snark.Snark;
|
||||
import org.klomp.snark.Storage;
|
||||
|
||||
/**
|
||||
* Comparators for various columns
|
||||
*
|
||||
* @since 0.9.16 from TorrentNameComparator, moved from I2PSnarkservlet
|
||||
*/
|
||||
class Sorters {
|
||||
|
||||
/**
|
||||
* Negative is reverse
|
||||
*
|
||||
*<ul>
|
||||
*<li>0, 1: Name
|
||||
*<li>2: Status
|
||||
*<li>3: Peers
|
||||
*<li>4: ETA
|
||||
*<li>5: Size
|
||||
*<li>6: Downloaded
|
||||
*<li>7: Uploaded
|
||||
*<li>8: Down rate
|
||||
*<li>9: Up rate
|
||||
*<li>10: Remaining (needed)
|
||||
*<li>11: Upload ratio
|
||||
*<li>12: File type
|
||||
*</ul>
|
||||
*
|
||||
* @param servlet for file type callback only
|
||||
*/
|
||||
public static Comparator<Snark> getComparator(int type, I2PSnarkServlet servlet) {
|
||||
boolean rev = type < 0;
|
||||
Comparator<Snark> rv;
|
||||
switch (type) {
|
||||
|
||||
case -1:
|
||||
case 0:
|
||||
case 1:
|
||||
default:
|
||||
rv = new TorrentNameComparator();
|
||||
if (rev)
|
||||
rv = Collections.reverseOrder(rv);
|
||||
break;
|
||||
|
||||
case -2:
|
||||
case 2:
|
||||
rv = new StatusComparator(rev);
|
||||
break;
|
||||
|
||||
case -3:
|
||||
case 3:
|
||||
rv = new PeersComparator(rev);
|
||||
break;
|
||||
|
||||
case -4:
|
||||
case 4:
|
||||
rv = new ETAComparator(rev);
|
||||
break;
|
||||
|
||||
case -5:
|
||||
case 5:
|
||||
rv = new SizeComparator(rev);
|
||||
break;
|
||||
|
||||
case -6:
|
||||
case 6:
|
||||
rv = new DownloadedComparator(rev);
|
||||
break;
|
||||
|
||||
case -7:
|
||||
case 7:
|
||||
rv = new UploadedComparator(rev);
|
||||
break;
|
||||
|
||||
case -8:
|
||||
case 8:
|
||||
rv = new DownRateComparator(rev);
|
||||
break;
|
||||
|
||||
case -9:
|
||||
case 9:
|
||||
rv = new UpRateComparator(rev);
|
||||
break;
|
||||
|
||||
case -10:
|
||||
case 10:
|
||||
rv = new RemainingComparator(rev);
|
||||
break;
|
||||
|
||||
case -11:
|
||||
case 11:
|
||||
rv = new RatioComparator(rev);
|
||||
break;
|
||||
|
||||
case -12:
|
||||
case 12:
|
||||
rv = new FileTypeComparator(rev, servlet);
|
||||
break;
|
||||
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort alphabetically in current locale, ignore case, ignore leading "the "
|
||||
* (I guess this is worth it, a lot of torrents start with "The "
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private static class TorrentNameComparator implements Comparator<Snark>, Serializable {
|
||||
|
||||
public int compare(Snark l, Snark r) {
|
||||
return comp(l, r);
|
||||
}
|
||||
|
||||
public static int comp(Snark l, Snark r) {
|
||||
// put downloads and magnets first
|
||||
if (l.getStorage() == null && r.getStorage() != null)
|
||||
return -1;
|
||||
if (l.getStorage() != null && r.getStorage() == null)
|
||||
return 1;
|
||||
String ls = l.getBaseName();
|
||||
String llc = ls.toLowerCase(Locale.US);
|
||||
if (llc.startsWith("the ") || llc.startsWith("the.") || llc.startsWith("the_"))
|
||||
ls = ls.substring(4);
|
||||
String rs = r.getBaseName();
|
||||
String rlc = rs.toLowerCase(Locale.US);
|
||||
if (rlc.startsWith("the ") || rlc.startsWith("the.") || rlc.startsWith("the_"))
|
||||
rs = rs.substring(4);
|
||||
return Collator.getInstance().compare(ls, rs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward or reverse sort, but the fallback is always forward
|
||||
*/
|
||||
private static abstract class Sort implements Comparator<Snark>, Serializable {
|
||||
|
||||
private final boolean _rev;
|
||||
|
||||
public Sort(boolean rev) {
|
||||
_rev = rev;
|
||||
}
|
||||
|
||||
public int compare(Snark l, Snark r) {
|
||||
int rv = compareIt(l, r);
|
||||
if (rv != 0)
|
||||
return _rev ? 0 - rv : rv;
|
||||
return TorrentNameComparator.comp(l, r);
|
||||
}
|
||||
|
||||
protected abstract int compareIt(Snark l, Snark r);
|
||||
|
||||
protected static int compLong(long l, long r) {
|
||||
if (l < r)
|
||||
return -1;
|
||||
if (l > r)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class StatusComparator extends Sort {
|
||||
|
||||
private StatusComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
int rv = getStatus(l) - getStatus(r);
|
||||
if (rv != 0)
|
||||
return rv;
|
||||
// use reverse remaining as first tie break
|
||||
return compLong(r.getNeededLength(), l.getNeededLength());
|
||||
}
|
||||
|
||||
private static int getStatus(Snark snark) {
|
||||
long remaining = snark.getRemainingLength();
|
||||
long needed = snark.getNeededLength();
|
||||
if (snark.isStopped()) {
|
||||
if (remaining < 0)
|
||||
return 0;
|
||||
if (remaining > 0)
|
||||
return 5;
|
||||
return 10;
|
||||
}
|
||||
if (snark.isStarting())
|
||||
return 15;
|
||||
if (snark.isAllocating())
|
||||
return 20;
|
||||
if (remaining < 0)
|
||||
return 15; // magnet
|
||||
if (remaining == 0)
|
||||
return 100;
|
||||
if (snark.isChecking())
|
||||
return 95;
|
||||
if (snark.getNeededLength() <= 0)
|
||||
return 90;
|
||||
if (snark.getPeerCount() <= 0)
|
||||
return 40;
|
||||
if (snark.getDownloadRate() <= 0)
|
||||
return 50;
|
||||
return 60;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PeersComparator extends Sort {
|
||||
|
||||
public PeersComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return l.getPeerCount() - r.getPeerCount();
|
||||
}
|
||||
}
|
||||
|
||||
private static class RemainingComparator extends Sort {
|
||||
|
||||
public RemainingComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getNeededLength(), r.getNeededLength());
|
||||
}
|
||||
}
|
||||
|
||||
private static class ETAComparator extends Sort {
|
||||
|
||||
public ETAComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(eta(l), eta(r));
|
||||
}
|
||||
|
||||
private static long eta(Snark snark) {
|
||||
long needed = snark.getNeededLength();
|
||||
if (needed <= 0)
|
||||
return 0;
|
||||
long total = snark.getTotalLength();
|
||||
if (needed > total)
|
||||
needed = total;
|
||||
long downBps = snark.getDownloadRate();
|
||||
if (downBps > 0)
|
||||
return needed / downBps;
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SizeComparator extends Sort {
|
||||
|
||||
public SizeComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getTotalLength(), r.getTotalLength());
|
||||
}
|
||||
}
|
||||
|
||||
private static class DownloadedComparator extends Sort {
|
||||
|
||||
public DownloadedComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
long ld = l.getTotalLength() - l.getRemainingLength();
|
||||
long rd = r.getTotalLength() - r.getRemainingLength();
|
||||
return compLong(ld, rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static class UploadedComparator extends Sort {
|
||||
|
||||
public UploadedComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getUploaded(), r.getUploaded());
|
||||
}
|
||||
}
|
||||
|
||||
private static class DownRateComparator extends Sort {
|
||||
|
||||
public DownRateComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getDownloadRate(), r.getDownloadRate());
|
||||
}
|
||||
}
|
||||
|
||||
private static class UpRateComparator extends Sort {
|
||||
|
||||
public UpRateComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getUploadRate(), r.getUploadRate());
|
||||
}
|
||||
}
|
||||
|
||||
private static class RatioComparator extends Sort {
|
||||
|
||||
private static final long M = 128 * 1024 * 1024;
|
||||
|
||||
public RatioComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
long lt = l.getTotalLength();
|
||||
long ld = lt > 0 ? ((M * l.getUploaded()) / lt) : 0;
|
||||
long rt = r.getTotalLength();
|
||||
long rd = rt > 0 ? ((M * r.getUploaded()) / rt) : 0;
|
||||
return compLong(ld, rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FileTypeComparator extends Sort {
|
||||
|
||||
private final I2PSnarkServlet servlet;
|
||||
|
||||
public FileTypeComparator(boolean rev, I2PSnarkServlet servlet) {
|
||||
super(rev);
|
||||
this.servlet = servlet;
|
||||
}
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
String ls = toName(l);
|
||||
String rs = toName(r);
|
||||
return ls.compareTo(rs);
|
||||
}
|
||||
|
||||
private String toName(Snark snark) {
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
if (meta == null)
|
||||
return "0";
|
||||
if (meta.getFiles() != null)
|
||||
return "1";
|
||||
// arbitrary sort based on icon name
|
||||
return servlet.toIcon(meta.getName());
|
||||
}
|
||||
}
|
||||
|
||||
////////////// Comparators for details page below
|
||||
|
||||
/**
|
||||
* Class to precompute and efficiently sort data
|
||||
* on a torrent file entry.
|
||||
*/
|
||||
public static class FileAndIndex {
|
||||
public final File file;
|
||||
public final boolean isDirectory;
|
||||
public final long length;
|
||||
public final long remaining;
|
||||
public final int priority;
|
||||
public final int index;
|
||||
|
||||
/**
|
||||
* @param storage may be null
|
||||
*/
|
||||
public FileAndIndex(File file, Storage storage) {
|
||||
this.file = file;
|
||||
index = storage != null ? storage.indexOf(file) : -1;
|
||||
if (index >= 0) {
|
||||
isDirectory = false;
|
||||
remaining = storage.remaining(index);
|
||||
priority = storage.getPriority(index);
|
||||
} else {
|
||||
isDirectory = file.isDirectory();
|
||||
remaining = -1;
|
||||
priority = -999;
|
||||
}
|
||||
length = isDirectory ? 0 : file.length();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Negative is reverse
|
||||
*
|
||||
*<ul>
|
||||
*<li>0, 1: Name
|
||||
*<li>5: Size
|
||||
*<li>10: Remaining (needed)
|
||||
*<li>12: File type
|
||||
*<li>13: Priority
|
||||
*</ul>
|
||||
*
|
||||
* @param servlet for file type callback only
|
||||
*/
|
||||
public static Comparator<FileAndIndex> getFileComparator(int type, I2PSnarkServlet servlet) {
|
||||
boolean rev = type < 0;
|
||||
Comparator<FileAndIndex> rv;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case -1:
|
||||
case 0:
|
||||
case 1:
|
||||
default:
|
||||
rv = new FileNameComparator();
|
||||
if (rev)
|
||||
rv = Collections.reverseOrder(rv);
|
||||
break;
|
||||
|
||||
case -5:
|
||||
case 5:
|
||||
rv = new FAISizeComparator(rev);
|
||||
break;
|
||||
|
||||
case -10:
|
||||
case 10:
|
||||
rv = new FAIRemainingComparator(rev);
|
||||
break;
|
||||
|
||||
case -12:
|
||||
case 12:
|
||||
rv = new FAITypeComparator(rev, servlet);
|
||||
break;
|
||||
|
||||
case -13:
|
||||
case 13:
|
||||
rv = new FAIPriorityComparator(rev);
|
||||
break;
|
||||
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort alphabetically in current locale, ignore case,
|
||||
* directories first
|
||||
* @since 0.9.6 moved from I2PSnarkServlet in 0.9.16
|
||||
*/
|
||||
private static class FileNameComparator implements Comparator<FileAndIndex>, Serializable {
|
||||
|
||||
public int compare(FileAndIndex l, FileAndIndex r) {
|
||||
return comp(l, r);
|
||||
}
|
||||
|
||||
public static int comp(FileAndIndex l, FileAndIndex r) {
|
||||
boolean ld = l.isDirectory;
|
||||
boolean rd = r.isDirectory;
|
||||
if (ld && !rd)
|
||||
return -1;
|
||||
if (rd && !ld)
|
||||
return 1;
|
||||
return Collator.getInstance().compare(l.file.getName(), r.file.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward or reverse sort, but the fallback is always forward
|
||||
*/
|
||||
private static abstract class FAISort implements Comparator<FileAndIndex>, Serializable {
|
||||
|
||||
private final boolean _rev;
|
||||
|
||||
public FAISort(boolean rev) {
|
||||
_rev = rev;
|
||||
}
|
||||
|
||||
public int compare(FileAndIndex l, FileAndIndex r) {
|
||||
int rv = compareIt(l, r);
|
||||
if (rv != 0)
|
||||
return _rev ? 0 - rv : rv;
|
||||
return FileNameComparator.comp(l, r);
|
||||
}
|
||||
|
||||
protected abstract int compareIt(FileAndIndex l, FileAndIndex r);
|
||||
|
||||
protected static int compLong(long l, long r) {
|
||||
if (l < r)
|
||||
return -1;
|
||||
if (l > r)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FAIRemainingComparator extends FAISort {
|
||||
|
||||
public FAIRemainingComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(FileAndIndex l, FileAndIndex r) {
|
||||
return compLong(l.remaining, r.remaining);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FAISizeComparator extends FAISort {
|
||||
|
||||
public FAISizeComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(FileAndIndex l, FileAndIndex r) {
|
||||
return compLong(l.length, r.length);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FAITypeComparator extends FAISort {
|
||||
|
||||
private final I2PSnarkServlet servlet;
|
||||
|
||||
public FAITypeComparator(boolean rev, I2PSnarkServlet servlet) {
|
||||
super(rev);
|
||||
this.servlet = servlet;
|
||||
}
|
||||
|
||||
public int compareIt(FileAndIndex l, FileAndIndex r) {
|
||||
String ls = toName(l);
|
||||
String rs = toName(r);
|
||||
return ls.compareTo(rs);
|
||||
}
|
||||
|
||||
private String toName(FileAndIndex fai) {
|
||||
if (fai.isDirectory)
|
||||
return "0";
|
||||
// arbitrary sort based on icon name
|
||||
return servlet.toIcon(fai.file.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static class FAIPriorityComparator extends FAISort {
|
||||
|
||||
public FAIPriorityComparator(boolean rev) { super(rev); }
|
||||
|
||||
/** highest first */
|
||||
public int compareIt(FileAndIndex l, FileAndIndex r) {
|
||||
return r.priority - l.priority;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ epub = application/epub+zip
|
||||
flac = audio/flac
|
||||
flv = video/x-flv
|
||||
iso = application/x-iso9660-image
|
||||
js = text/javascript
|
||||
m4a = audio/mp4a-latm
|
||||
m4v = video/x-m4v
|
||||
mkv = video/x-matroska
|
||||
|
Before Width: | Height: | Size: 464 B After Width: | Height: | Size: 464 B |
Before Width: | Height: | Size: 733 B After Width: | Height: | Size: 733 B |
Before Width: | Height: | Size: 587 B After Width: | Height: | Size: 587 B |
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 673 B |
Before Width: | Height: | Size: 882 B After Width: | Height: | Size: 882 B |
Before Width: | Height: | Size: 889 B After Width: | Height: | Size: 889 B |
Before Width: | Height: | Size: 766 B After Width: | Height: | Size: 766 B |
Before Width: | Height: | Size: 653 B After Width: | Height: | Size: 653 B |
Before Width: | Height: | Size: 537 B After Width: | Height: | Size: 537 B |
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 578 B |
BIN
apps/i2psnark/resources/icons/itoopie_xxsm.png
Normal file
After Width: | Height: | Size: 661 B |
Before Width: | Height: | Size: 591 B After Width: | Height: | Size: 591 B |
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 385 B |
Before Width: | Height: | Size: 853 B After Width: | Height: | Size: 853 B |
Before Width: | Height: | Size: 635 B After Width: | Height: | Size: 635 B |
Before Width: | Height: | Size: 294 B After Width: | Height: | Size: 294 B |
Before Width: | Height: | Size: 591 B After Width: | Height: | Size: 591 B |
Before Width: | Height: | Size: 589 B After Width: | Height: | Size: 589 B |
Before Width: | Height: | Size: 591 B After Width: | Height: | Size: 591 B |
Before Width: | Height: | Size: 537 B After Width: | Height: | Size: 537 B |
93
apps/i2psnark/resources/js/folder.js
Normal file
@ -0,0 +1,93 @@
|
||||
function setupbuttons() {
|
||||
updatesetallbuttons();
|
||||
var form = document.forms[0];
|
||||
form.savepri.disabled = true;
|
||||
form.savepri.className = 'disabled';
|
||||
}
|
||||
|
||||
function priorityclicked() {
|
||||
updatesetallbuttons();
|
||||
var form = document.forms[0];
|
||||
form.savepri.disabled = false;
|
||||
form.savepri.className = 'accept';
|
||||
}
|
||||
|
||||
function updatesetallbuttons() {
|
||||
var notNorm = false;
|
||||
var notHigh = false;
|
||||
var notSkip = false;
|
||||
var form = document.forms[0];
|
||||
for(i = 0; i < form.elements.length; i++) {
|
||||
var elem = form.elements[i];
|
||||
if (elem.type == 'radio') {
|
||||
if (!elem.checked) {
|
||||
if (elem.className == 'prinorm')
|
||||
notNorm = true;
|
||||
else if (elem.className == 'prihigh')
|
||||
notHigh = true;
|
||||
else
|
||||
notSkip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (notNorm)
|
||||
document.getElementById('setallnorm').className = 'control';
|
||||
else
|
||||
document.getElementById('setallnorm').className = 'controld';
|
||||
if (notHigh)
|
||||
document.getElementById('setallhigh').className = 'control';
|
||||
else
|
||||
document.getElementById('setallhigh').className = 'controld';
|
||||
if (notSkip)
|
||||
document.getElementById('setallskip').className = 'control';
|
||||
else
|
||||
document.getElementById('setallskip').className = 'controld';
|
||||
}
|
||||
|
||||
function setallnorm() {
|
||||
var form = document.forms[0];
|
||||
for(i = 0; i < form.elements.length; i++) {
|
||||
var elem = form.elements[i];
|
||||
if (elem.type == 'radio') {
|
||||
if (elem.className === 'prinorm')
|
||||
elem.checked = true;
|
||||
}
|
||||
}
|
||||
document.getElementById('setallnorm').className = 'controld';
|
||||
document.getElementById('setallhigh').className = 'control';
|
||||
document.getElementById('setallskip').className = 'control';
|
||||
form.savepri.disabled = false;
|
||||
form.savepri.className = 'accept';
|
||||
}
|
||||
|
||||
function setallhigh() {
|
||||
var form = document.forms[0];
|
||||
for(i = 0; i < form.elements.length; i++) {
|
||||
var elem = form.elements[i];
|
||||
if (elem.type == 'radio') {
|
||||
if (elem.className === 'prihigh')
|
||||
elem.checked = true;
|
||||
}
|
||||
}
|
||||
document.getElementById('setallnorm').className = 'control';
|
||||
document.getElementById('setallhigh').className = 'controld';
|
||||
document.getElementById('setallskip').className = 'control';
|
||||
form.savepri.disabled = false;
|
||||
form.savepri.className = 'accept';
|
||||
}
|
||||
|
||||
function setallskip() {
|
||||
var form = document.forms[0];
|
||||
for(i = 0; i < form.elements.length; i++) {
|
||||
var elem = form.elements[i];
|
||||
if (elem.type == 'radio') {
|
||||
if (elem.className === 'priskip')
|
||||
elem.checked = true;
|
||||
}
|
||||
}
|
||||
document.getElementById('setallnorm').className = 'control';
|
||||
document.getElementById('setallhigh').className = 'control';
|
||||
document.getElementById('setallskip').className = 'controld';
|
||||
form.savepri.disabled = false;
|
||||
form.savepri.className = 'accept';
|
||||
}
|
@ -40,6 +40,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
@ -256,7 +257,15 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
}
|
||||
|
||||
if (gui) {
|
||||
new I2PTunnelGUI(this);
|
||||
// removed from source, now in i2p.scripts
|
||||
//new I2PTunnelGUI(this);
|
||||
try {
|
||||
Class<?> cls = Class.forName("net.i2p.i2ptunnel.I2PTunnelGUI");
|
||||
Constructor<?> con = cls.getConstructor(I2PTunnel.class);
|
||||
con.newInstance(this);
|
||||
} catch (Throwable t) {
|
||||
throw new UnsupportedOperationException("GUI is not available, try -cli", t);
|
||||
}
|
||||
} else if (cli) {
|
||||
try {
|
||||
System.out.println("Enter 'help' for help.");
|
||||
|
@ -1,48 +0,0 @@
|
||||
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
|
||||
* (c) 2003 - 2004 mihi
|
||||
*/
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.Frame;
|
||||
import java.awt.TextArea;
|
||||
import java.awt.TextField;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
/**
|
||||
* AWT gui since kaffe doesn't support swing yet
|
||||
*/
|
||||
public class I2PTunnelGUI extends Frame implements ActionListener, Logging {
|
||||
|
||||
TextField input;
|
||||
TextArea log;
|
||||
I2PTunnel t;
|
||||
|
||||
public I2PTunnelGUI(I2PTunnel t) {
|
||||
super("I2PTunnel control panel");
|
||||
this.t = t;
|
||||
setLayout(new BorderLayout());
|
||||
add("South", input = new TextField());
|
||||
input.addActionListener(this);
|
||||
Font font = new Font("Monospaced", Font.PLAIN, 12);
|
||||
add("Center", log = new TextArea("", 20, 80, TextArea.SCROLLBARS_VERTICAL_ONLY));
|
||||
log.setFont(font);
|
||||
log.setEditable(false);
|
||||
log("enter 'help' for help.");
|
||||
pack();
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
public void log(String s) {
|
||||
log.append(s + "\n");
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
log("I2PTunnel>" + input.getText());
|
||||
t.runCommand(input.getText(), this);
|
||||
log("---");
|
||||
input.setText("");
|
||||
}
|
||||
}
|
@ -601,9 +601,12 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
|
||||
return;
|
||||
int status = ise != null ? ise.getStatus() : -1;
|
||||
String error;
|
||||
//TODO MessageStatusMessage.STATUS_SEND_FAILURE_UNSUPPORTED_ENCRYPTION
|
||||
if (status == MessageStatusMessage.STATUS_SEND_FAILURE_NO_LEASESET) {
|
||||
// We won't get this one unless it is treated as a hard failure
|
||||
// in streaming. See PacketQueue.java
|
||||
error = usingWWWProxy ? "nolsp" : "nols";
|
||||
} else if (status == MessageStatusMessage.STATUS_SEND_FAILURE_UNSUPPORTED_ENCRYPTION) {
|
||||
error = usingWWWProxy ? "encp" : "enc";
|
||||
} else {
|
||||
error = usingWWWProxy ? "dnfp" : "dnf";
|
||||
}
|
||||
|
@ -188,8 +188,7 @@ public class EditBean extends IndexBean {
|
||||
|
||||
/** @since 0.9.12 */
|
||||
public boolean isSigTypeAvailable(int code) {
|
||||
SigType type = SigType.getByCode(code);
|
||||
return type != null && type.isAvailable();
|
||||
return SigType.isAvailable(code);
|
||||
}
|
||||
|
||||
/** @since 0.8.9 */
|
||||
|
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="jetty">
|
||||
|
||||
<property name="jetty.ver" value="8.1.15.v20140411" />
|
||||
<property name="jetty.ver" value="8.1.16.v20140903" />
|
||||
<property name="jetty.base" value="jetty-distribution-${jetty.ver}" />
|
||||
<property name="jetty.sha1" value="41ec2b5e5605c038fb28d1f118669f06b4479e71" />
|
||||
<property name="jetty.sha1" value="5440b33a722d82b746b9ce50168bfce3c22af349" />
|
||||
<property name="jetty.filename" value="${jetty.base}.zip" />
|
||||
<property name="jetty.url" value="http://download.eclipse.org/jetty/${jetty.ver}/dist/${jetty.filename}" />
|
||||
<property name="verified.filename" value="verified.txt" />
|
||||
|
@ -5,7 +5,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.transport.TransportManager;
|
||||
|
@ -29,8 +29,8 @@ import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.Lease;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
import net.i2p.router.util.HashDistance; // debug
|
||||
@ -199,10 +199,10 @@ public class NetDbRenderer {
|
||||
FloodfillNetworkDatabaseFacade netdb = (FloodfillNetworkDatabaseFacade)_context.netDb();
|
||||
buf.append("<p><b>Total Leasesets: ").append(leases.size());
|
||||
buf.append("</b></p><p><b>Published (RAP) Leasesets: ").append(netdb.getKnownLeaseSets());
|
||||
buf.append("</b></p><p><b>Mod Data: \"").append(DataHelper.getUTF8(_context.routingKeyGenerator().getModData()))
|
||||
.append("\" Last Changed: ").append(new Date(_context.routingKeyGenerator().getLastChanged()));
|
||||
buf.append("</b></p><p><b>Next Mod Data: \"").append(DataHelper.getUTF8(_context.routingKeyGenerator().getNextModData()))
|
||||
.append("\" Change in: ").append(DataHelper.formatDuration(_context.routingKeyGenerator().getTimeTillMidnight()));
|
||||
buf.append("</b></p><p><b>Mod Data: \"").append(DataHelper.getUTF8(_context.routerKeyGenerator().getModData()))
|
||||
.append("\" Last Changed: ").append(new Date(_context.routerKeyGenerator().getLastChanged()));
|
||||
buf.append("</b></p><p><b>Next Mod Data: \"").append(DataHelper.getUTF8(_context.routerKeyGenerator().getNextModData()))
|
||||
.append("\" Change in: ").append(DataHelper.formatDuration(_context.routerKeyGenerator().getTimeTillMidnight()));
|
||||
int ff = _context.peerManager().getPeersByCapability(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL).size();
|
||||
buf.append("</b></p><p><b>Known Floodfills: ").append(ff);
|
||||
buf.append("</b></p><p><b>Currently Floodfill? ");
|
||||
@ -415,7 +415,9 @@ public class NetDbRenderer {
|
||||
// shouldnt happen
|
||||
buf.append("<b>" + _("Published") + ":</b> in ").append(DataHelper.formatDuration2(0-age)).append("???<br>\n");
|
||||
}
|
||||
buf.append("<b>" + _("Address(es)") + ":</b> ");
|
||||
buf.append("<b>").append(_("Signing Key")).append(":</b> ")
|
||||
.append(info.getIdentity().getSigningPublicKey().getType().toString());
|
||||
buf.append("<br>\n<b>" + _("Address(es)") + ":</b> ");
|
||||
String country = _context.commSystem().getCountry(info.getIdentity().getHash());
|
||||
if(country != null) {
|
||||
buf.append("<img height=\"11\" width=\"16\" alt=\"").append(country.toUpperCase(Locale.US)).append('\"');
|
||||
|
@ -10,7 +10,7 @@ import java.util.TreeSet;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.peermanager.DBHistory;
|
||||
import net.i2p.router.peermanager.PeerProfile;
|
||||
|
@ -3,8 +3,8 @@ package net.i2p.router.web;
|
||||
import java.util.Date;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.Signature;
|
||||
|
||||
/**
|
||||
|
@ -15,8 +15,8 @@ import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
|
@ -11,7 +11,7 @@ import java.util.Map;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
|
@ -44,7 +44,7 @@ class PacketQueue implements SendMessageStatusListener {
|
||||
private static final int FINAL_TAGS_TO_SEND = 4;
|
||||
private static final int FINAL_TAG_THRESHOLD = 2;
|
||||
private static final long REMOVE_EXPIRED_TIME = 67*1000;
|
||||
private static final boolean ENABLE_STATUS_LISTEN = false;
|
||||
private static final boolean ENABLE_STATUS_LISTEN = true;
|
||||
|
||||
public PacketQueue(I2PAppContext context, I2PSession session, ConnectionManager mgr) {
|
||||
_context = context;
|
||||
@ -267,6 +267,20 @@ class PacketQueue implements SendMessageStatusListener {
|
||||
_messageStatusMap.remove(id);
|
||||
break;
|
||||
|
||||
case MessageStatusMessage.STATUS_SEND_FAILURE_NO_LEASESET:
|
||||
// Ideally we would like to make this a hard failure,
|
||||
// but it caused far too many fast-fails that were then
|
||||
// resolved by the user clicking reload in his browser.
|
||||
// Until the LS fetch is faster and more reliable,
|
||||
// or we increase the timeout for it,
|
||||
// we can't treat this one as a hard fail.
|
||||
// Let the streaming retransmission paper over the problem.
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("LS lookup (soft) failure for msg " + msgId + " on " + con);
|
||||
_messageStatusMap.remove(id);
|
||||
break;
|
||||
|
||||
|
||||
case MessageStatusMessage.STATUS_SEND_FAILURE_LOCAL:
|
||||
case MessageStatusMessage.STATUS_SEND_FAILURE_ROUTER:
|
||||
case MessageStatusMessage.STATUS_SEND_FAILURE_NETWORK:
|
||||
@ -280,7 +294,6 @@ class PacketQueue implements SendMessageStatusListener {
|
||||
case MessageStatusMessage.STATUS_SEND_FAILURE_DESTINATION:
|
||||
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_LEASESET:
|
||||
case MessageStatusMessage.STATUS_SEND_FAILURE_EXPIRED_LEASESET:
|
||||
case MessageStatusMessage.STATUS_SEND_FAILURE_NO_LEASESET:
|
||||
case SendMessageStatusListener.STATUS_CANCELLED:
|
||||
if (con.getHighestAckedThrough() >= 0) {
|
||||
// a retxed SYN succeeded before the first SYN failed
|
||||
|
@ -548,7 +548,7 @@
|
||||
windowtitle="I2P Anonymous Network - Java Documentation - Version ${release.number}">
|
||||
<group title="Core SDK (i2p.jar)" packages="net.i2p:net.i2p.*:net.i2p.client:net.i2p.client.*:net.i2p.internal:net.i2p.internal.*:freenet.support.CPUInformation:org.bouncycastle.oldcrypto:org.bouncycastle.oldcrypto.*:gnu.crypto.*:gnu.getopt:gnu.gettext:com.nettgryppa.security:net.metanotion:net.metanotion.*" />
|
||||
<group title="Streaming Library" packages="net.i2p.client.streaming:net.i2p.client.streaming.impl" />
|
||||
<group title="Router" packages="net.i2p.router:net.i2p.router.*:net.i2p.data.i2np:org.cybergarage.*:org.freenetproject:org.xlattice.crypto.filters" />
|
||||
<group title="Router" packages="net.i2p.router:net.i2p.router.*:net.i2p.data.i2np:net.i2p.data.router:org.cybergarage.*:org.freenetproject:org.xlattice.crypto.filters" />
|
||||
<group title="Router Console" packages="net.i2p.router.web:net.i2p.router.update" />
|
||||
<!-- apps and bridges starting here, alphabetical please -->
|
||||
<group title="Addressbook Application" packages="net.i2p.addressbook" />
|
||||
|
@ -86,7 +86,6 @@ public class I2PAppContext {
|
||||
private SHA256Generator _sha;
|
||||
protected Clock _clock; // overridden in RouterContext
|
||||
private DSAEngine _dsa;
|
||||
private RoutingKeyGenerator _routingKeyGenerator;
|
||||
private RandomSource _random;
|
||||
private KeyGenerator _keyGenerator;
|
||||
protected KeyRing _keyRing; // overridden in RouterContext
|
||||
@ -106,7 +105,6 @@ public class I2PAppContext {
|
||||
private volatile boolean _shaInitialized;
|
||||
protected volatile boolean _clockInitialized; // used in RouterContext
|
||||
private volatile boolean _dsaInitialized;
|
||||
private volatile boolean _routingKeyGeneratorInitialized;
|
||||
private volatile boolean _randomInitialized;
|
||||
private volatile boolean _keyGeneratorInitialized;
|
||||
protected volatile boolean _keyRingInitialized; // used in RouterContext
|
||||
@ -126,7 +124,7 @@ public class I2PAppContext {
|
||||
private final Object _lock1 = new Object(), _lock2 = new Object(), _lock3 = new Object(), _lock4 = new Object(),
|
||||
_lock5 = new Object(), _lock6 = new Object(), _lock7 = new Object(), _lock8 = new Object(),
|
||||
_lock9 = new Object(), _lock10 = new Object(), _lock11 = new Object(), _lock12 = new Object(),
|
||||
_lock13 = new Object(), _lock14 = new Object(), _lock15 = new Object(), _lock16 = new Object(),
|
||||
_lock13 = new Object(), _lock14 = new Object(), _lock16 = new Object(),
|
||||
_lock17 = new Object(), _lock18 = new Object(), _lock19 = new Object(), _lock20 = new Object();
|
||||
|
||||
/**
|
||||
@ -851,19 +849,13 @@ public class I2PAppContext {
|
||||
* may want to test out how things react when peers don't agree on
|
||||
* how to skew.
|
||||
*
|
||||
* As of 0.9.16, returns null in I2PAppContext.
|
||||
* You must be in RouterContext to get a generator.
|
||||
*
|
||||
* @return null always
|
||||
*/
|
||||
public RoutingKeyGenerator routingKeyGenerator() {
|
||||
if (!_routingKeyGeneratorInitialized)
|
||||
initializeRoutingKeyGenerator();
|
||||
return _routingKeyGenerator;
|
||||
}
|
||||
|
||||
private void initializeRoutingKeyGenerator() {
|
||||
synchronized (_lock15) {
|
||||
if (_routingKeyGenerator == null)
|
||||
_routingKeyGenerator = new RoutingKeyGenerator(this);
|
||||
_routingKeyGeneratorInitialized = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
135
core/java/src/net/i2p/crypto/ECUtil.java
Normal file
@ -0,0 +1,135 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.spec.ECField;
|
||||
import java.security.spec.ECFieldFp;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.EllipticCurve;
|
||||
|
||||
import net.i2p.util.NativeBigInteger;
|
||||
|
||||
/**
|
||||
* Used by KeyGenerator.getSigningPublicKey()
|
||||
*
|
||||
* Modified from
|
||||
* http://stackoverflow.com/questions/15727147/scalar-multiplication-of-point-over-elliptic-curve
|
||||
* Apparently public domain.
|
||||
* Supported P-192 only.
|
||||
* Added curve parameters to support all curves.
|
||||
*
|
||||
* @since 0.9.16
|
||||
*/
|
||||
class ECUtil {
|
||||
|
||||
private static final BigInteger TWO = new BigInteger("2");
|
||||
private static final BigInteger THREE = new BigInteger("3");
|
||||
|
||||
public static ECPoint scalarMult(ECPoint p, BigInteger kin, EllipticCurve curve) {
|
||||
ECPoint r = ECPoint.POINT_INFINITY;
|
||||
BigInteger prime = ((ECFieldFp) curve.getField()).getP();
|
||||
BigInteger k = kin.mod(prime);
|
||||
int length = k.bitLength();
|
||||
byte[] binarray = new byte[length];
|
||||
for (int i = 0; i <= length-1; i++) {
|
||||
binarray[i] = k.mod(TWO).byteValue();
|
||||
k = k.divide(TWO);
|
||||
}
|
||||
|
||||
for (int i = length-1; i >= 0; i--) {
|
||||
// i should start at length-1 not -2 because the MSB of binarry may not be 1
|
||||
r = doublePoint(r, curve);
|
||||
if (binarray[i] == 1)
|
||||
r = addPoint(r, p, curve);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static ECPoint addPoint(ECPoint r, ECPoint s, EllipticCurve curve) {
|
||||
if (r.equals(s))
|
||||
return doublePoint(r, curve);
|
||||
else if (r.equals(ECPoint.POINT_INFINITY))
|
||||
return s;
|
||||
else if (s.equals(ECPoint.POINT_INFINITY))
|
||||
return r;
|
||||
BigInteger prime = ((ECFieldFp) curve.getField()).getP();
|
||||
BigInteger slope = (r.getAffineY().subtract(s.getAffineY())).multiply(r.getAffineX().subtract(s.getAffineX()).modInverse(prime)).mod(prime);
|
||||
slope = new NativeBigInteger(slope);
|
||||
BigInteger xOut = (slope.modPow(TWO, prime).subtract(r.getAffineX())).subtract(s.getAffineX()).mod(prime);
|
||||
BigInteger yOut = s.getAffineY().negate().mod(prime);
|
||||
yOut = yOut.add(slope.multiply(s.getAffineX().subtract(xOut))).mod(prime);
|
||||
ECPoint out = new ECPoint(xOut, yOut);
|
||||
return out;
|
||||
}
|
||||
|
||||
private static ECPoint doublePoint(ECPoint r, EllipticCurve curve) {
|
||||
if (r.equals(ECPoint.POINT_INFINITY))
|
||||
return r;
|
||||
BigInteger slope = (r.getAffineX().pow(2)).multiply(THREE);
|
||||
slope = slope.add(curve.getA());
|
||||
BigInteger prime = ((ECFieldFp) curve.getField()).getP();
|
||||
slope = slope.multiply((r.getAffineY().multiply(TWO)).modInverse(prime));
|
||||
BigInteger xOut = slope.pow(2).subtract(r.getAffineX().multiply(TWO)).mod(prime);
|
||||
BigInteger yOut = (r.getAffineY().negate()).add(slope.multiply(r.getAffineX().subtract(xOut))).mod(prime);
|
||||
ECPoint out = new ECPoint(xOut, yOut);
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* P-192 test only.
|
||||
* See KeyGenerator.main() for a test of all supported curves.
|
||||
*/
|
||||
/****
|
||||
public static void main(String[] args) {
|
||||
EllipticCurve P192 = ECConstants.P192_SPEC.getCurve();
|
||||
BigInteger xs = new BigInteger("d458e7d127ae671b0c330266d246769353a012073e97acf8", 16);
|
||||
BigInteger ys = new BigInteger("325930500d851f336bddc050cf7fb11b5673a1645086df3b", 16);
|
||||
BigInteger xt = new BigInteger("f22c4395213e9ebe67ddecdd87fdbd01be16fb059b9753a4", 16);
|
||||
BigInteger yt = new BigInteger("264424096af2b3597796db48f8dfb41fa9cecc97691a9c79", 16);
|
||||
ECPoint S = new ECPoint(xs,ys);
|
||||
ECPoint T = new ECPoint(xt,yt);
|
||||
|
||||
// Verifying addition
|
||||
ECPoint Rst = addPoint(S, T, P192);
|
||||
BigInteger xst = new BigInteger("48e1e4096b9b8e5ca9d0f1f077b8abf58e843894de4d0290", 16); // Specified value of x of point R for addition in NIST Routine example
|
||||
System.out.println("x-coordinate of point Rst is : " + Rst.getAffineX());
|
||||
System.out.println("y-coordinate of point Rst is : " + Rst.getAffineY());
|
||||
if (Rst.getAffineX().equals(xst))
|
||||
System.out.println("Adding is correct");
|
||||
else
|
||||
System.out.println("Adding FAIL");
|
||||
|
||||
//Verifying Doubling
|
||||
BigInteger xr = new BigInteger("30c5bc6b8c7da25354b373dc14dd8a0eba42d25a3f6e6962", 16); // Specified value of x of point R for doubling in NIST Routine example
|
||||
BigInteger yr = new BigInteger("0dde14bc4249a721c407aedbf011e2ddbbcb2968c9d889cf", 16);
|
||||
ECPoint R2s = new ECPoint(xr, yr); // Specified value of y of point R for doubling in NIST Routine example
|
||||
System.out.println("x-coordinate of point R2s is : " + R2s.getAffineX());
|
||||
System.out.println("y-coordinate of point R2s is : " + R2s.getAffineY());
|
||||
System.out.println("x-coordinate of calculated point is : " + doublePoint(S, P192).getAffineX());
|
||||
System.out.println("y-coordinate of calculated point is : " + doublePoint(S, P192).getAffineY());
|
||||
if (R2s.getAffineX().equals(doublePoint(S, P192).getAffineX()) &&
|
||||
R2s.getAffineY().equals(doublePoint(S, P192).getAffineY()))
|
||||
System.out.println("Doubling is correct");
|
||||
else
|
||||
System.out.println("Doubling FAIL");
|
||||
|
||||
xr = new BigInteger("1faee4205a4f669d2d0a8f25e3bcec9a62a6952965bf6d31", 16); // Specified value of x of point R for scalar Multiplication in NIST Routine example
|
||||
yr = new BigInteger("5ff2cdfa508a2581892367087c696f179e7a4d7e8260fb06", 16); // Specified value of y of point R for scalar Multiplication in NIST Routine example
|
||||
ECPoint Rds = new ECPoint(xr, yr);
|
||||
BigInteger d = new BigInteger("a78a236d60baec0c5dd41b33a542463a8255391af64c74ee", 16);
|
||||
|
||||
ECPoint Rs = scalarMult(S, d, P192);
|
||||
|
||||
System.out.println("x-coordinate of point Rds is : " + Rds.getAffineX());
|
||||
System.out.println("y-coordinate of point Rds is : " + Rds.getAffineY());
|
||||
System.out.println("x-coordinate of calculated point is : " + Rs.getAffineX());
|
||||
System.out.println("y-coordinate of calculated point is : " + Rs.getAffineY());
|
||||
|
||||
|
||||
if (Rds.getAffineX().equals(Rs.getAffineX()) &&
|
||||
Rds.getAffineY().equals(Rs.getAffineY()))
|
||||
System.out.println("Scalar Multiplication is correct");
|
||||
else
|
||||
System.out.println("Scalar Multiplication FAIL");
|
||||
}
|
||||
****/
|
||||
}
|
@ -57,6 +57,9 @@ public class ElGamalEngine {
|
||||
private final I2PAppContext _context;
|
||||
private final YKGenerator _ykgen;
|
||||
|
||||
private static final BigInteger ELGPM1 = CryptoConstants.elgp.subtract(BigInteger.ONE);
|
||||
|
||||
|
||||
/**
|
||||
* The ElGamal engine should only be constructed and accessed through the
|
||||
* application context. This constructor should only be used by the
|
||||
@ -171,10 +174,11 @@ public class ElGamalEngine {
|
||||
if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to encrypt ElGamal block (" + diff + "ms)");
|
||||
}
|
||||
|
||||
_context.statManager().addRateData("crypto.elGamal.encrypt", diff, 0);
|
||||
_context.statManager().addRateData("crypto.elGamal.encrypt", diff);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/** Decrypt the data
|
||||
* @param encrypted encrypted data, must be exactly 514 bytes
|
||||
* Contains the two-part encrypted data starting at bytes 0 and 257.
|
||||
@ -184,26 +188,26 @@ public class ElGamalEngine {
|
||||
* @return unencrypted data or null on failure
|
||||
*/
|
||||
public byte[] decrypt(byte encrypted[], PrivateKey privateKey) {
|
||||
// actually it must be exactly 514 bytes or the arraycopy below will AIOOBE
|
||||
if ((encrypted == null) || (encrypted.length > 514))
|
||||
throw new IllegalArgumentException("Data to decrypt must be <= 514 bytes at the moment");
|
||||
if ((encrypted == null) || (encrypted.length != 514))
|
||||
throw new IllegalArgumentException("Data to decrypt must be exactly 514 bytes");
|
||||
long start = _context.clock().now();
|
||||
|
||||
byte[] ybytes = new byte[257];
|
||||
byte[] dbytes = new byte[257];
|
||||
System.arraycopy(encrypted, 0, ybytes, 0, 257);
|
||||
System.arraycopy(encrypted, 257, dbytes, 0, 257);
|
||||
BigInteger y = new NativeBigInteger(1, ybytes);
|
||||
BigInteger d = new NativeBigInteger(1, dbytes);
|
||||
BigInteger a = new NativeBigInteger(1, privateKey.getData());
|
||||
BigInteger y1p = CryptoConstants.elgp.subtract(BigInteger.ONE).subtract(a);
|
||||
BigInteger y1p = ELGPM1.subtract(a);
|
||||
// we use this buf first for Y, then for D, then for the hash
|
||||
byte[] buf = SimpleByteCache.acquire(257);
|
||||
System.arraycopy(encrypted, 0, buf, 0, 257);
|
||||
BigInteger y = new NativeBigInteger(1, buf);
|
||||
BigInteger ya = y.modPow(y1p, CryptoConstants.elgp);
|
||||
System.arraycopy(encrypted, 257, buf, 0, 257);
|
||||
BigInteger d = new NativeBigInteger(1, buf);
|
||||
BigInteger m = ya.multiply(d);
|
||||
m = m.mod(CryptoConstants.elgp);
|
||||
byte val[] = m.toByteArray();
|
||||
int i = 0;
|
||||
for (i = 0; i < val.length; i++)
|
||||
int i;
|
||||
for (i = 0; i < val.length; i++) {
|
||||
if (val[i] != (byte) 0x00) break;
|
||||
}
|
||||
|
||||
int payloadLen = val.length - i - 1 - Hash.HASH_LENGTH;
|
||||
if (payloadLen < 0) {
|
||||
@ -220,10 +224,10 @@ public class ElGamalEngine {
|
||||
byte rv[] = new byte[payloadLen];
|
||||
System.arraycopy(val, i + 1 + Hash.HASH_LENGTH, rv, 0, rv.length);
|
||||
|
||||
byte[] calcHash = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
||||
_context.sha().calculateHash(rv, 0, payloadLen, calcHash, 0);
|
||||
boolean ok = DataHelper.eq(calcHash, 0, val, i + 1, Hash.HASH_LENGTH);
|
||||
SimpleByteCache.release(calcHash);
|
||||
// we reuse buf here for the calculated hash
|
||||
_context.sha().calculateHash(rv, 0, payloadLen, buf, 0);
|
||||
boolean ok = DataHelper.eq(buf, 0, val, i + 1, Hash.HASH_LENGTH);
|
||||
SimpleByteCache.release(buf);
|
||||
|
||||
long end = _context.clock().now();
|
||||
|
||||
@ -233,7 +237,7 @@ public class ElGamalEngine {
|
||||
_log.warn("Took too long to decrypt and verify ElGamal block (" + diff + "ms)");
|
||||
}
|
||||
|
||||
_context.statManager().addRateData("crypto.elGamal.decrypt", diff, 0);
|
||||
_context.statManager().addRateData("crypto.elGamal.decrypt", diff);
|
||||
|
||||
if (ok) {
|
||||
//_log.debug("Hash matches: " + DataHelper.toString(hash.getData(), hash.getData().length));
|
||||
|
@ -12,11 +12,25 @@ package net.i2p.crypto;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.ProviderException;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.security.spec.EllipticCurve;
|
||||
import java.security.spec.RSAKeyGenParameterSpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.PublicKey;
|
||||
@ -268,24 +282,56 @@ public class KeyGenerator {
|
||||
}
|
||||
|
||||
/** Convert a SigningPrivateKey to a SigningPublicKey.
|
||||
* DSA-SHA1 only.
|
||||
* As of 0.9.16, supports all key types.
|
||||
*
|
||||
* @param priv a SigningPrivateKey object
|
||||
* @return a SigningPublicKey object
|
||||
* @throws IllegalArgumentException on bad key
|
||||
* @throws IllegalArgumentException on bad key or unknown type
|
||||
*/
|
||||
public static SigningPublicKey getSigningPublicKey(SigningPrivateKey priv) {
|
||||
if (priv.getType() != SigType.DSA_SHA1)
|
||||
throw new IllegalArgumentException();
|
||||
SigType type = priv.getType();
|
||||
if (type == null)
|
||||
throw new IllegalArgumentException("Unknown type");
|
||||
try {
|
||||
switch (type.getBaseAlgorithm()) {
|
||||
case DSA:
|
||||
BigInteger x = new NativeBigInteger(1, priv.toByteArray());
|
||||
BigInteger y = CryptoConstants.dsag.modPow(x, CryptoConstants.dsap);
|
||||
SigningPublicKey pub = new SigningPublicKey();
|
||||
try {
|
||||
pub.setData(SigUtil.rectify(y, SigningPublicKey.KEYSIZE_BYTES));
|
||||
} catch (InvalidKeyException ike) {
|
||||
throw new IllegalArgumentException(ike);
|
||||
}
|
||||
return pub;
|
||||
|
||||
case EC:
|
||||
ECPrivateKey ecpriv = SigUtil.toJavaECKey(priv);
|
||||
BigInteger s = ecpriv.getS();
|
||||
ECParameterSpec spec = (ECParameterSpec) type.getParams();
|
||||
EllipticCurve curve = spec.getCurve();
|
||||
ECPoint g = spec.getGenerator();
|
||||
ECPoint w = ECUtil.scalarMult(g, s, curve);
|
||||
ECPublicKeySpec ecks = new ECPublicKeySpec(w, ecpriv.getParams());
|
||||
KeyFactory eckf = KeyFactory.getInstance("EC");
|
||||
ECPublicKey ecpub = (ECPublicKey) eckf.generatePublic(ecks);
|
||||
return SigUtil.fromJavaKey(ecpub, type);
|
||||
|
||||
case RSA:
|
||||
RSAPrivateKey rsapriv = SigUtil.toJavaRSAKey(priv);
|
||||
BigInteger exp = ((RSAKeyGenParameterSpec)type.getParams()).getPublicExponent();
|
||||
RSAPublicKeySpec rsaks = new RSAPublicKeySpec(rsapriv.getModulus(), exp);
|
||||
KeyFactory rsakf = KeyFactory.getInstance("RSA");
|
||||
RSAPublicKey rsapub = (RSAPublicKey) rsakf.generatePublic(rsaks);
|
||||
return SigUtil.fromJavaKey(rsapub, type);
|
||||
|
||||
case EdDSA:
|
||||
EdDSAPrivateKey epriv = SigUtil.toJavaEdDSAKey(priv);
|
||||
EdDSAPublicKey epub = new EdDSAPublicKey(new EdDSAPublicKeySpec(epriv.getA(), epriv.getParams()));
|
||||
return SigUtil.fromJavaKey(epub, type);
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported algorithm");
|
||||
}
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new IllegalArgumentException("Conversion failed", gse);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
@ -322,14 +368,20 @@ public class KeyGenerator {
|
||||
long stime = 0;
|
||||
long vtime = 0;
|
||||
SimpleDataStructure keys[] = KeyGenerator.getInstance().generateSigningKeys(type);
|
||||
//System.out.println("pubkey " + keys[0]);
|
||||
SigningPublicKey pubkey = (SigningPublicKey) keys[0];
|
||||
SigningPrivateKey privkey = (SigningPrivateKey) keys[1];
|
||||
SigningPublicKey pubkey2 = getSigningPublicKey(privkey);
|
||||
if (pubkey.equals(pubkey2))
|
||||
System.out.println(type + " private-to-public test PASSED");
|
||||
else
|
||||
System.out.println(type + " private-to-public test FAILED");
|
||||
//System.out.println("privkey " + keys[1]);
|
||||
for (int i = 0; i < runs; i++) {
|
||||
RandomSource.getInstance().nextBytes(src);
|
||||
long start = System.nanoTime();
|
||||
Signature sig = DSAEngine.getInstance().sign(src, (SigningPrivateKey) keys[1]);
|
||||
Signature sig = DSAEngine.getInstance().sign(src, privkey);
|
||||
long mid = System.nanoTime();
|
||||
boolean ok = DSAEngine.getInstance().verifySignature(sig, src, (SigningPublicKey) keys[0]);
|
||||
boolean ok = DSAEngine.getInstance().verifySignature(sig, src, pubkey);
|
||||
long end = System.nanoTime();
|
||||
stime += mid - start;
|
||||
vtime += end - mid;
|
||||
|
@ -345,7 +345,7 @@ public class SigUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated unused
|
||||
*
|
||||
*/
|
||||
public static RSAPrivateKey toJavaRSAKey(SigningPrivateKey pk)
|
||||
throws GeneralSecurityException {
|
||||
@ -358,7 +358,7 @@ public class SigUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated unused
|
||||
*
|
||||
*/
|
||||
public static SigningPublicKey fromJavaKey(RSAPublicKey pk, SigType type)
|
||||
throws GeneralSecurityException {
|
||||
|
@ -24,20 +24,15 @@ import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
@ -638,13 +633,17 @@ public class DataHelper {
|
||||
* Integers are a fixed number of bytes (numBytes), stored as unsigned integers in network byte order.
|
||||
* @param value value to write out, non-negative
|
||||
* @param rawStream stream to write to
|
||||
* @param numBytes number of bytes to write the number into (padding as necessary)
|
||||
* @throws DataFormatException if value is negative
|
||||
* @param numBytes number of bytes to write the number into, 1-8 (padding as necessary)
|
||||
* @throws DataFormatException if value is negative or if numBytes not 1-8
|
||||
* @throws IOException if there is an IO error writing to the stream
|
||||
*/
|
||||
public static void writeLong(OutputStream rawStream, int numBytes, long value)
|
||||
throws DataFormatException, IOException {
|
||||
if (value < 0) throw new DataFormatException("Value is negative (" + value + ")");
|
||||
if (numBytes <= 0 || numBytes > 8)
|
||||
// probably got the args backwards
|
||||
throw new DataFormatException("Bad byte count " + numBytes);
|
||||
if (value < 0)
|
||||
throw new DataFormatException("Value is negative (" + value + ")");
|
||||
for (int i = (numBytes - 1) * 8; i >= 0; i -= 8) {
|
||||
byte cur = (byte) (value >> i);
|
||||
rawStream.write(cur);
|
||||
@ -667,7 +666,7 @@ public class DataHelper {
|
||||
* @param value non-negative
|
||||
*/
|
||||
public static void toLong(byte target[], int offset, int numBytes, long value) throws IllegalArgumentException {
|
||||
if (numBytes <= 0) throw new IllegalArgumentException("Invalid number of bytes");
|
||||
if (numBytes <= 0 || numBytes > 8) throw new IllegalArgumentException("Invalid number of bytes");
|
||||
if (value < 0) throw new IllegalArgumentException("Negative value not allowed");
|
||||
|
||||
for (int i = offset + numBytes - 1; i >= offset; i--) {
|
||||
@ -1425,58 +1424,6 @@ public class DataHelper {
|
||||
out.write(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort based on the Hash of the DataStructure.
|
||||
* Warning - relatively slow.
|
||||
* WARNING - this sort order must be consistent network-wide, so while the order is arbitrary,
|
||||
* it cannot be changed.
|
||||
* Why? Just because it has to be consistent so signing will work.
|
||||
* How to spec as returning the same type as the param?
|
||||
* DEPRECATED - Only used by RouterInfo.
|
||||
*
|
||||
* @return a new list
|
||||
*/
|
||||
public static List<? extends DataStructure> sortStructures(Collection<? extends DataStructure> dataStructures) {
|
||||
if (dataStructures == null) return Collections.emptyList();
|
||||
|
||||
// This used to use Hash.toString(), which is insane, since a change to toString()
|
||||
// would break the whole network. Now use Hash.toBase64().
|
||||
// Note that the Base64 sort order is NOT the same as the raw byte sort order,
|
||||
// despite what you may read elsewhere.
|
||||
|
||||
//ArrayList<DataStructure> rv = new ArrayList(dataStructures.size());
|
||||
//TreeMap<String, DataStructure> tm = new TreeMap();
|
||||
//for (DataStructure struct : dataStructures) {
|
||||
// tm.put(struct.calculateHash().toString(), struct);
|
||||
//}
|
||||
//for (DataStructure struct : tm.values()) {
|
||||
// rv.add(struct);
|
||||
//}
|
||||
ArrayList<DataStructure> rv = new ArrayList<DataStructure>(dataStructures);
|
||||
sortStructureList(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* See above.
|
||||
* DEPRECATED - Only used by RouterInfo.
|
||||
*
|
||||
* @since 0.9
|
||||
*/
|
||||
static void sortStructureList(List<? extends DataStructure> dataStructures) {
|
||||
Collections.sort(dataStructures, new DataStructureComparator());
|
||||
}
|
||||
|
||||
/**
|
||||
* See sortStructures() comments.
|
||||
* @since 0.8.3
|
||||
*/
|
||||
private static class DataStructureComparator implements Comparator<DataStructure>, Serializable {
|
||||
public int compare(DataStructure l, DataStructure r) {
|
||||
return l.calculateHash().toBase64().compareTo(r.calculateHash().toBase64());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: formatDuration2() recommended in most cases for readability
|
||||
*/
|
||||
|
@ -11,6 +11,7 @@ package net.i2p.data;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.DSAEngine;
|
||||
|
||||
/**
|
||||
@ -47,7 +48,7 @@ public abstract class DatabaseEntry extends DataStructureImpl {
|
||||
|
||||
protected volatile Signature _signature;
|
||||
protected volatile Hash _currentRoutingKey;
|
||||
protected volatile byte[] _routingKeyGenMod;
|
||||
protected volatile long _routingKeyGenMod;
|
||||
|
||||
/**
|
||||
* A common interface to the timestamp of the two subclasses.
|
||||
@ -106,11 +107,15 @@ public abstract class DatabaseEntry extends DataStructureImpl {
|
||||
* Get the routing key for the structure using the current modifier in the RoutingKeyGenerator.
|
||||
* This only calculates a new one when necessary though (if the generator's key modifier changes)
|
||||
*
|
||||
* @throws IllegalStateException if not in RouterContext
|
||||
*/
|
||||
public Hash getRoutingKey() {
|
||||
RoutingKeyGenerator gen = RoutingKeyGenerator.getInstance();
|
||||
byte[] mod = gen.getModData();
|
||||
if (!Arrays.equals(mod, _routingKeyGenMod)) {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
if (!ctx.isRouterContext())
|
||||
throw new IllegalStateException("Not in router context");
|
||||
RoutingKeyGenerator gen = ctx.routingKeyGenerator();
|
||||
long mod = gen.getLastChanged();
|
||||
if (mod != _routingKeyGenMod) {
|
||||
_currentRoutingKey = gen.getRoutingKey(getHash());
|
||||
_routingKeyGenMod = mod;
|
||||
}
|
||||
@ -124,9 +129,16 @@ public abstract class DatabaseEntry extends DataStructureImpl {
|
||||
_currentRoutingKey = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if not in RouterContext
|
||||
*/
|
||||
public boolean validateRoutingKey() {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
if (!ctx.isRouterContext())
|
||||
throw new IllegalStateException("Not in router context");
|
||||
RoutingKeyGenerator gen = ctx.routingKeyGenerator();
|
||||
Hash destKey = getHash();
|
||||
Hash rk = RoutingKeyGenerator.getInstance().getRoutingKey(destKey);
|
||||
Hash rk = gen.getRoutingKey(destKey);
|
||||
return rk.equals(getRoutingKey());
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,13 @@ public class KeysAndCert extends DataStructureImpl {
|
||||
_signingKey = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.16
|
||||
*/
|
||||
public byte[] getPadding() {
|
||||
return _padding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if was already set
|
||||
* @since 0.9.12
|
||||
@ -114,6 +121,8 @@ public class KeysAndCert extends DataStructureImpl {
|
||||
_publicKey.writeBytes(out);
|
||||
if (_padding != null)
|
||||
out.write(_padding);
|
||||
else if (_signingKey.length() < SigningPublicKey.KEYSIZE_BYTES)
|
||||
throw new DataFormatException("No padding set");
|
||||
_signingKey.writeTruncatedBytes(out);
|
||||
_certificate.writeBytes(out);
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ public class PrivateKey extends SimpleDataStructure {
|
||||
/** derives a new PublicKey object derived from the secret contents
|
||||
* of this PrivateKey
|
||||
* @return a PublicKey object
|
||||
* @throws IllegalArgumentException on bad key
|
||||
*/
|
||||
public PublicKey toPublic() {
|
||||
return KeyGenerator.getPublicKey(this);
|
||||
|
@ -1,11 +1,13 @@
|
||||
package net.i2p.data;
|
||||
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@ -24,6 +26,7 @@ import net.i2p.crypto.DSAEngine;
|
||||
import net.i2p.crypto.KeyGenerator;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
/**
|
||||
* This helper class reads and writes files in the
|
||||
@ -48,11 +51,11 @@ public class PrivateKeyFile {
|
||||
|
||||
private static final int HASH_EFFORT = VerifiedDestination.MIN_HASHCASH_EFFORT;
|
||||
|
||||
private final File file;
|
||||
protected final File file;
|
||||
private final I2PClient client;
|
||||
private Destination dest;
|
||||
private PrivateKey privKey;
|
||||
private SigningPrivateKey signingPrivKey;
|
||||
protected Destination dest;
|
||||
protected PrivateKey privKey;
|
||||
protected SigningPrivateKey signingPrivKey;
|
||||
|
||||
/**
|
||||
* Create a new PrivateKeyFile, or modify an existing one, with various
|
||||
@ -224,6 +227,16 @@ public class PrivateKeyFile {
|
||||
*/
|
||||
public PrivateKeyFile(File file, PublicKey pubkey, SigningPublicKey spubkey, Certificate cert,
|
||||
PrivateKey pk, SigningPrivateKey spk) {
|
||||
this(file, pubkey, spubkey, cert, pk, spk, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param padding null OK, must be non-null if spubkey length < 128
|
||||
* @throws IllegalArgumentException on mismatch of spubkey and spk types
|
||||
* @since 0.9.16
|
||||
*/
|
||||
public PrivateKeyFile(File file, PublicKey pubkey, SigningPublicKey spubkey, Certificate cert,
|
||||
PrivateKey pk, SigningPrivateKey spk, byte[] padding) {
|
||||
if (spubkey.getType() != spk.getType())
|
||||
throw new IllegalArgumentException("Signing key type mismatch");
|
||||
this.file = file;
|
||||
@ -232,6 +245,8 @@ public class PrivateKeyFile {
|
||||
this.dest.setPublicKey(pubkey);
|
||||
this.dest.setSigningPublicKey(spubkey);
|
||||
this.dest.setCertificate(cert);
|
||||
if (padding != null)
|
||||
this.dest.setPadding(padding);
|
||||
this.privKey = pk;
|
||||
this.signingPrivKey = spk;
|
||||
}
|
||||
@ -241,9 +256,9 @@ public class PrivateKeyFile {
|
||||
*/
|
||||
public Destination createIfAbsent() throws I2PException, IOException, DataFormatException {
|
||||
if(!this.file.exists()) {
|
||||
FileOutputStream out = null;
|
||||
OutputStream out = null;
|
||||
try {
|
||||
out = new FileOutputStream(this.file);
|
||||
out = new SecureFileOutputStream(this.file);
|
||||
if (this.client != null)
|
||||
this.client.createDestination(out);
|
||||
else
|
||||
@ -257,7 +272,10 @@ public class PrivateKeyFile {
|
||||
return getDestination();
|
||||
}
|
||||
|
||||
/** Also sets the local privKey and signingPrivKey */
|
||||
/**
|
||||
* If the destination is not set, read it in from the file.
|
||||
* Also sets the local privKey and signingPrivKey.
|
||||
*/
|
||||
public Destination getDestination() throws I2PSessionException, IOException, DataFormatException {
|
||||
if (dest == null) {
|
||||
I2PSession s = open();
|
||||
@ -408,9 +426,9 @@ public class PrivateKeyFile {
|
||||
}
|
||||
|
||||
public I2PSession open(Properties opts) throws I2PSessionException, IOException {
|
||||
FileInputStream in = null;
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(this.file);
|
||||
in = new BufferedInputStream(new FileInputStream(this.file));
|
||||
I2PSession s = this.client.createSession(in, opts);
|
||||
return s;
|
||||
} finally {
|
||||
@ -424,13 +442,12 @@ public class PrivateKeyFile {
|
||||
* Copied from I2PClientImpl.createDestination()
|
||||
*/
|
||||
public void write() throws IOException, DataFormatException {
|
||||
FileOutputStream out = null;
|
||||
OutputStream out = null;
|
||||
try {
|
||||
out = new FileOutputStream(this.file);
|
||||
out = new SecureFileOutputStream(this.file);
|
||||
this.dest.writeBytes(out);
|
||||
this.privKey.writeBytes(out);
|
||||
this.signingPrivKey.writeBytes(out);
|
||||
out.flush();
|
||||
} finally {
|
||||
if (out != null) {
|
||||
try { out.close(); } catch (IOException ioe) {}
|
||||
@ -438,6 +455,23 @@ public class PrivateKeyFile {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the PublicKey matches the PrivateKey, and
|
||||
* the SigningPublicKey matches the SigningPrivateKey.
|
||||
*
|
||||
* @return success
|
||||
* @since 0.9.16
|
||||
*/
|
||||
public boolean validateKeyPairs() {
|
||||
try {
|
||||
if (!dest.getPublicKey().equals(KeyGenerator.getPublicKey(privKey)))
|
||||
return false;
|
||||
return dest.getSigningPublicKey().equals(KeyGenerator.getSigningPublicKey(signingPrivKey));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder s = new StringBuilder(128);
|
||||
|
@ -9,215 +9,40 @@ package net.i2p.data;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Arrays;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.util.HexDump;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Component to manage the munging of hashes into routing keys - given a hash,
|
||||
* perform some consistent transformation against it and return the result.
|
||||
* This transformation is fed by the current "mod data".
|
||||
*
|
||||
* Right now the mod data is the current date (GMT) as a string: "yyyyMMdd",
|
||||
* and the transformation takes the original hash, appends the bytes of that mod data,
|
||||
* then returns the SHA256 of that concatenation.
|
||||
*
|
||||
* Do we want this to simply do the XOR of the SHA256 of the current mod data and
|
||||
* the key? does that provide the randomization we need? It'd save an SHA256 op.
|
||||
* Bah, too much effort to think about for so little gain. Other algorithms may come
|
||||
* into play layer on about making periodic updates to the routing key for data elements
|
||||
* to mess with Sybil. This may be good enough though.
|
||||
*
|
||||
* Also - the method generateDateBasedModData() should be called after midnight GMT
|
||||
* once per day to generate the correct routing keys!
|
||||
*
|
||||
* Warning - API subject to change. Not for use outside the router.
|
||||
* As of 0.9.16, this is essentially just an interface.
|
||||
* Implementation moved to net.i2p.data.router.RouterKeyGenerator.
|
||||
* No generator is available in I2PAppContext; you must be in RouterContext.
|
||||
*
|
||||
*/
|
||||
public class RoutingKeyGenerator {
|
||||
private final Log _log;
|
||||
private final I2PAppContext _context;
|
||||
|
||||
public RoutingKeyGenerator(I2PAppContext context) {
|
||||
_log = context.logManager().getLog(RoutingKeyGenerator.class);
|
||||
_context = context;
|
||||
// ensure non-null mod data
|
||||
generateDateBasedModData();
|
||||
}
|
||||
public abstract class RoutingKeyGenerator {
|
||||
|
||||
/**
|
||||
* Get the generator for this context.
|
||||
*
|
||||
* @return null in I2PAppContext; non-null in RouterContext.
|
||||
*/
|
||||
public static RoutingKeyGenerator getInstance() {
|
||||
return I2PAppContext.getGlobalContext().routingKeyGenerator();
|
||||
}
|
||||
|
||||
private volatile byte _currentModData[];
|
||||
private volatile byte _nextModData[];
|
||||
private volatile long _nextMidnight;
|
||||
private volatile long _lastChanged;
|
||||
|
||||
private final static Calendar _cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
private static final String FORMAT = "yyyyMMdd";
|
||||
private static final int LENGTH = FORMAT.length();
|
||||
private final static SimpleDateFormat _fmt = new SimpleDateFormat(FORMAT, Locale.US);
|
||||
static {
|
||||
// make sure GMT is set, azi2phelper Vuze plugin is disabling static JVM TZ setting in Router.java
|
||||
_fmt.setCalendar(_cal);
|
||||
}
|
||||
|
||||
/**
|
||||
* The current (today's) mod data.
|
||||
* Warning - not a copy, do not corrupt.
|
||||
*
|
||||
* @return non-null, 8 bytes
|
||||
* The version of the current (today's) mod data.
|
||||
* Use to determine if the routing key should be regenerated.
|
||||
*/
|
||||
public byte[] getModData() {
|
||||
return _currentModData;
|
||||
}
|
||||
public abstract long getLastChanged();
|
||||
|
||||
/**
|
||||
* Tomorrow's mod data.
|
||||
* Warning - not a copy, do not corrupt.
|
||||
* For debugging use only.
|
||||
*
|
||||
* @return non-null, 8 bytes
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public byte[] getNextModData() {
|
||||
return _nextModData;
|
||||
}
|
||||
|
||||
public long getLastChanged() {
|
||||
return _lastChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* How long until midnight (ms)
|
||||
*
|
||||
* @return could be slightly negative
|
||||
* @since 0.9.10 moved from UpdateRoutingKeyModifierJob
|
||||
*/
|
||||
public long getTimeTillMidnight() {
|
||||
return _nextMidnight - _context.clock().now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set _cal to midnight for the time given.
|
||||
* Caller must synch.
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void setCalToPreviousMidnight(long now) {
|
||||
_cal.setTime(new Date(now));
|
||||
_cal.set(Calendar.YEAR, _cal.get(Calendar.YEAR)); // gcj <= 4.0 workaround
|
||||
_cal.set(Calendar.DAY_OF_YEAR, _cal.get(Calendar.DAY_OF_YEAR)); // gcj <= 4.0 workaround
|
||||
_cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
_cal.set(Calendar.MINUTE, 0);
|
||||
_cal.set(Calendar.SECOND, 0);
|
||||
_cal.set(Calendar.MILLISECOND, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate mod data from _cal.
|
||||
* Caller must synch.
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private byte[] generateModDataFromCal() {
|
||||
Date today = _cal.getTime();
|
||||
|
||||
String modVal = _fmt.format(today);
|
||||
if (modVal.length() != LENGTH)
|
||||
throw new IllegalStateException();
|
||||
byte[] mod = new byte[LENGTH];
|
||||
for (int i = 0; i < LENGTH; i++)
|
||||
mod[i] = (byte)(modVal.charAt(i) & 0xFF);
|
||||
return mod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current modifier data with some bytes derived from the current
|
||||
* date (yyyyMMdd in GMT)
|
||||
*
|
||||
* @return true if changed
|
||||
*/
|
||||
public synchronized boolean generateDateBasedModData() {
|
||||
long now = _context.clock().now();
|
||||
setCalToPreviousMidnight(now);
|
||||
byte[] mod = generateModDataFromCal();
|
||||
boolean changed = !Arrays.equals(_currentModData, mod);
|
||||
if (changed) {
|
||||
// add a day and store next midnight and mod data for convenience
|
||||
_cal.add(Calendar.DATE, 1);
|
||||
_nextMidnight = _cal.getTime().getTime();
|
||||
byte[] next = generateModDataFromCal();
|
||||
_currentModData = mod;
|
||||
_nextModData = next;
|
||||
_lastChanged = now;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Routing modifier generated: " + HexDump.dump(mod));
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a modified (yet consistent) hash from the origKey by generating the
|
||||
* SHA256 of the targetKey with the current modData appended to it
|
||||
*
|
||||
* This makes Sybil's job a lot harder, as she needs to essentially take over the
|
||||
* whole keyspace.
|
||||
* Get the routing key for a key.
|
||||
*
|
||||
* @throws IllegalArgumentException if origKey is null
|
||||
*/
|
||||
public Hash getRoutingKey(Hash origKey) {
|
||||
return getKey(origKey, _currentModData);
|
||||
}
|
||||
public abstract Hash getRoutingKey(Hash origKey);
|
||||
|
||||
/**
|
||||
* Get the routing key using tomorrow's modData, not today's
|
||||
*
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public Hash getNextRoutingKey(Hash origKey) {
|
||||
return getKey(origKey, _nextModData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a modified (yet consistent) hash from the origKey by generating the
|
||||
* SHA256 of the targetKey with the specified modData appended to it
|
||||
*
|
||||
* @throws IllegalArgumentException if origKey is null
|
||||
*/
|
||||
private static Hash getKey(Hash origKey, byte[] modData) {
|
||||
if (origKey == null) throw new IllegalArgumentException("Original key is null");
|
||||
byte modVal[] = new byte[Hash.HASH_LENGTH + LENGTH];
|
||||
System.arraycopy(origKey.getData(), 0, modVal, 0, Hash.HASH_LENGTH);
|
||||
System.arraycopy(modData, 0, modVal, Hash.HASH_LENGTH, LENGTH);
|
||||
return SHA256Generator.getInstance().calculateHash(modVal);
|
||||
}
|
||||
|
||||
/****
|
||||
public static void main(String args[]) {
|
||||
Hash k1 = new Hash();
|
||||
byte k1d[] = new byte[Hash.HASH_LENGTH];
|
||||
RandomSource.getInstance().nextBytes(k1d);
|
||||
k1.setData(k1d);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
System.out.println("K1: " + k1);
|
||||
Hash k1m = RoutingKeyGenerator.getInstance().getRoutingKey(k1);
|
||||
System.out.println("MOD: " + new String(RoutingKeyGenerator.getInstance().getModData()));
|
||||
System.out.println("K1M: " + k1m);
|
||||
}
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (Throwable t) { // nop
|
||||
}
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
@ -75,8 +75,12 @@ public class SigningPrivateKey extends SimpleDataStructure {
|
||||
return _type;
|
||||
}
|
||||
|
||||
/** converts this signing private key to its public equivalent
|
||||
/**
|
||||
* Converts this signing private key to its public equivalent.
|
||||
* As of 0.9.16, supports all key types.
|
||||
*
|
||||
* @return a SigningPublicKey object derived from this private key
|
||||
* @throws IllegalArgumentException on bad key or unknown or unsupported type
|
||||
*/
|
||||
public SigningPublicKey toPublic() {
|
||||
return KeyGenerator.getSigningPublicKey(this);
|
||||
|
@ -70,9 +70,12 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
@Override
|
||||
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||
throw new RuntimeException("go away, we dont want any");
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,9 +101,12 @@ public class SendMessageMessage extends I2CPMessageImpl {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
@Override
|
||||
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||
throw new RuntimeException("wtf, dont run me");
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
|
24
installer/resources/proxy/enc-header.ht
Normal file
@ -0,0 +1,24 @@
|
||||
HTTP/1.1 504 Gateway Timeout
|
||||
Content-Type: text/html; charset=UTF-8
|
||||
Cache-control: no-cache
|
||||
Connection: close
|
||||
Proxy-Connection: close
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html><head>
|
||||
<title>_("Warning: Eepsite Unreachable")</title>
|
||||
<link rel="shortcut icon" href="http://proxy.i2p/themes/console/images/favicon.ico">
|
||||
<link href="http://proxy.i2p/themes/console/default/console.css" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="logo">
|
||||
<a href="http://127.0.0.1:7657/" title="_("Router Console")"><img src="http://proxy.i2p/themes/console/images/i2plogo.png" alt="_("I2P Router Console")" border="0"></a><hr>
|
||||
<a href="http://127.0.0.1:7657/config.jsp">_("Configuration")</a> <a href="http://127.0.0.1:7657/help.jsp">_("Help")</a> <a href="http://127.0.0.1:7657/susidns/index">_("Addressbook")</a>
|
||||
</div>
|
||||
<div class="warning" id="warning">
|
||||
<h3>_("Warning: Eepsite Unreachable")</h3>
|
||||
<p>
|
||||
_("The eepsite was not reachable, because it uses encryption options that are not supported by your I2P or Java version.")
|
||||
<hr>
|
||||
<p><b>_("Could not connect to the following destination:")</b>
|
||||
</p>
|
25
installer/resources/proxy/encp-header.ht
Normal file
@ -0,0 +1,25 @@
|
||||
HTTP/1.1 504 Gateway Timeout
|
||||
Content-Type: text/html; charset=UTF-8
|
||||
Cache-control: no-cache
|
||||
Connection: close
|
||||
Proxy-Connection: close
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html><head>
|
||||
<title>_("Warning: Outproxy Unreachable")</title>
|
||||
<link rel="shortcut icon" href="http://proxy.i2p/themes/console/images/favicon.ico">
|
||||
<link href="http://proxy.i2p/themes/console/default/console.css" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="logo">
|
||||
<a href="http://127.0.0.1:7657/" title="_("Router Console")"><img src="http://proxy.i2p/themes/console/images/i2plogo.png" alt="_("I2P Router Console")" border="0"></a><hr>
|
||||
<a href="http://127.0.0.1:7657/config.jsp">_("Configuration")</a> <a href="http://127.0.0.1:7657/help.jsp">_("Help")</a> <a href="http://127.0.0.1:7657/susidns/index">_("Addressbook")</a>
|
||||
</div>
|
||||
<div class="warning" id="warning">
|
||||
<h3>_("Warning: Outproxy Unreachable")</h3>
|
||||
<p>
|
||||
_("The HTTP outproxy was not reachable, because it uses encryption options that are not supported by your I2P or Java version.")
|
||||
_("You may want to {0}retry{1} as this will randomly reselect an outproxy from the pool you have defined {2}here{3} (if you have more than one configured).", "<a href=\"javascript:parent.window.location.reload()\">", "</a>", "<a href=\"http://127.0.0.1:7657/i2ptunnel/index.jsp\">", "</a>")
|
||||
_("If you continue to have trouble you may want to edit your outproxy list {0}here{1}.", "<a href=\"http://127.0.0.1:7657/i2ptunnel/edit.jsp?tunnel=0\">", "</a>")
|
||||
</p>
|
||||
<hr><p><b>_("Could not connect to the following destination:")</b></p>
|
@ -513,6 +513,40 @@ a:active {
|
||||
color: #77b;
|
||||
}
|
||||
|
||||
a.control, a.controld {
|
||||
background: #fff;
|
||||
border: 1px inset #191;
|
||||
border-radius: 4px;
|
||||
color: #359;
|
||||
font-weight: bold;
|
||||
margin: 2px 4px;
|
||||
padding: 3px 4px;
|
||||
text-shadow: 0px 0px #410;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
a.controld {
|
||||
color: #459;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
a.control img, a.controld img {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a.control:hover {
|
||||
background-color: #559;
|
||||
border: 1px outset #559;
|
||||
color: #fff;
|
||||
text-shadow: 0px 1px 5px #410;
|
||||
}
|
||||
|
||||
a.control:active {
|
||||
background: #f60 !important;
|
||||
color: #fff !important;
|
||||
text-shadow: 0 !important;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 9pt;
|
||||
font-weight: bold;
|
||||
@ -594,6 +628,14 @@ input[type=radio] {
|
||||
|
||||
input.default { width: 1px; height: 1px; visibility: hidden; }
|
||||
|
||||
input.disabled, input.disabled:hover {
|
||||
background-color: #fff;
|
||||
border: 1px inset #191;
|
||||
color: #459;
|
||||
font-weight: normal;
|
||||
text-shadow: 0px 0px 0px #410;
|
||||
}
|
||||
|
||||
select {
|
||||
background: #fff !important;
|
||||
color: #22f;
|
||||
|
@ -366,6 +366,10 @@ table.snarkTorrents tbody tr:hover, table.snarkDirInfo tbody tr:hover {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
td.snarkFileIcon:first-child {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.snarkFileName {
|
||||
padding: 4px 0px !important;
|
||||
text-align: left !important;
|
||||
@ -526,6 +530,40 @@ a:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
a.control, a.controld {
|
||||
background: #989;
|
||||
border: 1px inset #bbb;
|
||||
border-radius: 4px;
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
margin: 2px 4px;
|
||||
padding: 3px 4px;
|
||||
text-shadow: 0px 0px #410;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
a.controld {
|
||||
color: #444;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
a.controld img {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a.control:hover {
|
||||
background-color: #f60;
|
||||
border: 1px outset #bbb;
|
||||
color: #fff;
|
||||
text-shadow: 0px 1px 5px #f00;
|
||||
}
|
||||
|
||||
a.control:active {
|
||||
background: #000 !important;
|
||||
color: #f60 !important;
|
||||
text-shadow: 0 !important;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 8.5pt;
|
||||
font-weight: bold;
|
||||
@ -598,6 +636,14 @@ input[type=radio] {
|
||||
|
||||
input.default { width: 1px; height: 1px; visibility: hidden; }
|
||||
|
||||
input.disabled, input.disabled:hover {
|
||||
background-color: #989;
|
||||
border: 1px inset #bbb;
|
||||
color: #444;
|
||||
font-weight: normal;
|
||||
text-shadow: 0px 0px 0px #444;
|
||||
}
|
||||
|
||||
input.accept {
|
||||
background: #989 url('../../console/images/accept.png') no-repeat 2px center;
|
||||
padding: 2px 3px 2px 20px !important;
|
||||
|
@ -373,6 +373,10 @@ td:first-child {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
td.snarkFileIcon:first-child {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.snarkFileName {
|
||||
padding: 4px 0px !important;
|
||||
text-align: left !important;
|
||||
@ -543,6 +547,40 @@ a:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
a.control, a.controld {
|
||||
background: #fef url('images/bling.png') repeat-x scroll center center;
|
||||
border: 1px inset #bbb;
|
||||
border-radius: 4px;
|
||||
color: #f30;
|
||||
font-weight: bold;
|
||||
margin: 2px 4px;
|
||||
padding: 3px 4px;
|
||||
text-shadow: 0px 0px #410;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
a.controld {
|
||||
color: #f60;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
a.controld img {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a.control:hover {
|
||||
background-color: #fef;
|
||||
border: 1px outset #bbb;
|
||||
color: #f60;
|
||||
text-shadow: 0px 1px 5px #fdf;
|
||||
}
|
||||
|
||||
a.control:active {
|
||||
background: #000 !important;
|
||||
color: #f60 !important;
|
||||
text-shadow: 0 !important;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 9pt;
|
||||
font-weight: bold;
|
||||
@ -612,6 +650,14 @@ input[type=radio] {
|
||||
|
||||
input.default { width: 1px; height: 1px; visibility: hidden; }
|
||||
|
||||
input.disabled, input.disabled:hover {
|
||||
background-color: #989;
|
||||
border: 1px inset #bbb;
|
||||
color: #f60;
|
||||
font-weight: normal;
|
||||
text-shadow: 0px 0px 0px #410;
|
||||
}
|
||||
|
||||
input.accept {
|
||||
background: #f3efc7 url('../../console/images/accept.png') no-repeat 2px center;
|
||||
padding: 2px 3px 2px 20px !important;
|
||||
|
@ -146,10 +146,10 @@ public class BuildRequestRecord {
|
||||
return (_data.getData()[_data.getOffset() + OFF_FLAG] & FLAG_OUTBOUND_ENDPOINT) != 0;
|
||||
}
|
||||
/**
|
||||
* Time that the request was sent, truncated to the nearest hour
|
||||
* Time that the request was sent (ms), truncated to the nearest hour
|
||||
*/
|
||||
public long readRequestTime() {
|
||||
return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_REQ_TIME, 4) * 60l * 60l * 1000l;
|
||||
return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_REQ_TIME, 4) * (60 * 60 * 1000L);
|
||||
}
|
||||
/**
|
||||
* What message ID should we send the request to the next hop with. If this is the outbound tunnel endpoint,
|
||||
@ -250,6 +250,8 @@ public class BuildRequestRecord {
|
||||
else if (isOutEndpoint)
|
||||
buf[OFF_FLAG] |= FLAG_OUTBOUND_ENDPOINT;
|
||||
long truncatedHour = ctx.clock().now();
|
||||
// prevent hop identification at top of the hour
|
||||
truncatedHour -= ctx.random().nextInt(90*1000);
|
||||
truncatedHour /= (60l*60l*1000l);
|
||||
DataHelper.toLong(buf, OFF_REQ_TIME, 4, truncatedHour);
|
||||
DataHelper.toLong(buf, OFF_SEND_MSG_ID, 4, nextMsgId);
|
||||
|
@ -17,7 +17,7 @@ import java.util.Set;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.SessionTag;
|
||||
import net.i2p.data.TunnelId;
|
||||
|
@ -18,7 +18,7 @@ import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.TunnelId;
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.i2p.data;
|
||||
package net.i2p.data.router;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
@ -17,6 +17,9 @@ import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.util.Addresses;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
|
||||
@ -36,6 +39,7 @@ import net.i2p.util.OrderedProperties;
|
||||
* several releases for the change to propagate as it is backwards-incompatible.
|
||||
* Restored as of 0.9.12.
|
||||
*
|
||||
* @since 0.9.16 moved from net.i2p.data
|
||||
* @author jrandom
|
||||
*/
|
||||
public class RouterAddress extends DataStructureImpl {
|
@ -1,4 +1,7 @@
|
||||
package net.i2p.data;
|
||||
package net.i2p.data.router;
|
||||
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.KeysAndCert;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
@ -16,6 +19,7 @@ package net.i2p.data;
|
||||
* As of 0.9.9 this data structure is immutable after the two keys and the certificate
|
||||
* are set; attempts to change them will throw an IllegalStateException.
|
||||
*
|
||||
* @since 0.9.16 moved from net.i2p.data
|
||||
* @author jrandom
|
||||
*/
|
||||
public class RouterIdentity extends KeysAndCert {
|
@ -1,4 +1,4 @@
|
||||
package net.i2p.data;
|
||||
package net.i2p.data.router;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
@ -31,6 +31,13 @@ import net.i2p.crypto.SHA1;
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DatabaseEntry;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.KeysAndCert;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
@ -47,6 +54,7 @@ import net.i2p.util.SystemVersion;
|
||||
* To ensure integrity of the RouterInfo, methods that change an element of the
|
||||
* RouterInfo will throw an IllegalStateException after the RouterInfo is signed.
|
||||
*
|
||||
* @since 0.9.16 moved from net.i2p.data
|
||||
* @author jrandom
|
||||
*/
|
||||
public class RouterInfo extends DatabaseEntry {
|
||||
@ -190,7 +198,7 @@ public class RouterInfo extends DatabaseEntry {
|
||||
// WARNING this sort algorithm cannot be changed, as it must be consistent
|
||||
// network-wide. The signature is not checked at readin time, but only
|
||||
// later, and the addresses are stored in a Set, not a List.
|
||||
DataHelper.sortStructureList(_addresses);
|
||||
SortHelper.sortStructureList(_addresses);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -308,7 +316,7 @@ public class RouterInfo extends DatabaseEntry {
|
||||
// WARNING this sort algorithm cannot be changed, as it must be consistent
|
||||
// network-wide. The signature is not checked at readin time, but only
|
||||
// later, and the hashes are stored in a Set, not a List.
|
||||
peers = (Collection<Hash>) DataHelper.sortStructures(peers);
|
||||
peers = (Collection<Hash>) SortHelper.sortStructures(peers);
|
||||
for (Hash peerHash : peers) {
|
||||
peerHash.writeBytes(out);
|
||||
}
|
224
router/java/src/net/i2p/data/router/RouterKeyGenerator.java
Normal file
@ -0,0 +1,224 @@
|
||||
package net.i2p.data.router;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2003 and released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* your children, but it might. Use at your own risk.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Arrays;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RoutingKeyGenerator;
|
||||
import net.i2p.util.HexDump;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Component to manage the munging of hashes into routing keys - given a hash,
|
||||
* perform some consistent transformation against it and return the result.
|
||||
* This transformation is fed by the current "mod data".
|
||||
*
|
||||
* Right now the mod data is the current date (GMT) as a string: "yyyyMMdd",
|
||||
* and the transformation takes the original hash, appends the bytes of that mod data,
|
||||
* then returns the SHA256 of that concatenation.
|
||||
*
|
||||
* Do we want this to simply do the XOR of the SHA256 of the current mod data and
|
||||
* the key? does that provide the randomization we need? It'd save an SHA256 op.
|
||||
* Bah, too much effort to think about for so little gain. Other algorithms may come
|
||||
* into play layer on about making periodic updates to the routing key for data elements
|
||||
* to mess with Sybil. This may be good enough though.
|
||||
*
|
||||
* Also - the method generateDateBasedModData() should be called after midnight GMT
|
||||
* once per day to generate the correct routing keys!
|
||||
*
|
||||
* @since 0.9.16 moved from net.i2p.data.RoutingKeyGenerator..
|
||||
*
|
||||
*/
|
||||
public class RouterKeyGenerator extends RoutingKeyGenerator {
|
||||
private final Log _log;
|
||||
private final I2PAppContext _context;
|
||||
|
||||
public RouterKeyGenerator(I2PAppContext context) {
|
||||
_log = context.logManager().getLog(RoutingKeyGenerator.class);
|
||||
_context = context;
|
||||
// ensure non-null mod data
|
||||
generateDateBasedModData();
|
||||
}
|
||||
|
||||
private volatile byte _currentModData[];
|
||||
private volatile byte _nextModData[];
|
||||
private volatile long _nextMidnight;
|
||||
private volatile long _lastChanged;
|
||||
|
||||
private final static Calendar _cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
private static final String FORMAT = "yyyyMMdd";
|
||||
private static final int LENGTH = FORMAT.length();
|
||||
private final static SimpleDateFormat _fmt = new SimpleDateFormat(FORMAT, Locale.US);
|
||||
static {
|
||||
// make sure GMT is set, azi2phelper Vuze plugin is disabling static JVM TZ setting in Router.java
|
||||
_fmt.setCalendar(_cal);
|
||||
}
|
||||
|
||||
/**
|
||||
* The current (today's) mod data.
|
||||
* Warning - not a copy, do not corrupt.
|
||||
*
|
||||
* @return non-null, 8 bytes
|
||||
*/
|
||||
public byte[] getModData() {
|
||||
return _currentModData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tomorrow's mod data.
|
||||
* Warning - not a copy, do not corrupt.
|
||||
* For debugging use only.
|
||||
*
|
||||
* @return non-null, 8 bytes
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public byte[] getNextModData() {
|
||||
return _nextModData;
|
||||
}
|
||||
|
||||
public long getLastChanged() {
|
||||
return _lastChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* How long until midnight (ms)
|
||||
*
|
||||
* @return could be slightly negative
|
||||
* @since 0.9.10 moved from UpdateRoutingKeyModifierJob
|
||||
*/
|
||||
public long getTimeTillMidnight() {
|
||||
return _nextMidnight - _context.clock().now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set _cal to midnight for the time given.
|
||||
* Caller must synch.
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void setCalToPreviousMidnight(long now) {
|
||||
_cal.setTime(new Date(now));
|
||||
_cal.set(Calendar.YEAR, _cal.get(Calendar.YEAR)); // gcj <= 4.0 workaround
|
||||
_cal.set(Calendar.DAY_OF_YEAR, _cal.get(Calendar.DAY_OF_YEAR)); // gcj <= 4.0 workaround
|
||||
_cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
_cal.set(Calendar.MINUTE, 0);
|
||||
_cal.set(Calendar.SECOND, 0);
|
||||
_cal.set(Calendar.MILLISECOND, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate mod data from _cal.
|
||||
* Caller must synch.
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private byte[] generateModDataFromCal() {
|
||||
Date today = _cal.getTime();
|
||||
|
||||
String modVal = _fmt.format(today);
|
||||
if (modVal.length() != LENGTH)
|
||||
throw new IllegalStateException();
|
||||
byte[] mod = new byte[LENGTH];
|
||||
for (int i = 0; i < LENGTH; i++)
|
||||
mod[i] = (byte)(modVal.charAt(i) & 0xFF);
|
||||
return mod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current modifier data with some bytes derived from the current
|
||||
* date (yyyyMMdd in GMT)
|
||||
*
|
||||
* @return true if changed
|
||||
*/
|
||||
public synchronized boolean generateDateBasedModData() {
|
||||
long now = _context.clock().now();
|
||||
setCalToPreviousMidnight(now);
|
||||
byte[] mod = generateModDataFromCal();
|
||||
boolean changed = !Arrays.equals(_currentModData, mod);
|
||||
if (changed) {
|
||||
// add a day and store next midnight and mod data for convenience
|
||||
_cal.add(Calendar.DATE, 1);
|
||||
_nextMidnight = _cal.getTime().getTime();
|
||||
byte[] next = generateModDataFromCal();
|
||||
_currentModData = mod;
|
||||
_nextModData = next;
|
||||
// ensure version is bumped
|
||||
if (_lastChanged == now)
|
||||
now++;
|
||||
_lastChanged = now;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Routing modifier generated: " + HexDump.dump(mod));
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a modified (yet consistent) hash from the origKey by generating the
|
||||
* SHA256 of the targetKey with the current modData appended to it
|
||||
*
|
||||
* This makes Sybil's job a lot harder, as she needs to essentially take over the
|
||||
* whole keyspace.
|
||||
*
|
||||
* @throws IllegalArgumentException if origKey is null
|
||||
*/
|
||||
public Hash getRoutingKey(Hash origKey) {
|
||||
return getKey(origKey, _currentModData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the routing key using tomorrow's modData, not today's
|
||||
*
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public Hash getNextRoutingKey(Hash origKey) {
|
||||
return getKey(origKey, _nextModData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a modified (yet consistent) hash from the origKey by generating the
|
||||
* SHA256 of the targetKey with the specified modData appended to it
|
||||
*
|
||||
* @throws IllegalArgumentException if origKey is null
|
||||
*/
|
||||
private static Hash getKey(Hash origKey, byte[] modData) {
|
||||
if (origKey == null) throw new IllegalArgumentException("Original key is null");
|
||||
byte modVal[] = new byte[Hash.HASH_LENGTH + LENGTH];
|
||||
System.arraycopy(origKey.getData(), 0, modVal, 0, Hash.HASH_LENGTH);
|
||||
System.arraycopy(modData, 0, modVal, Hash.HASH_LENGTH, LENGTH);
|
||||
return SHA256Generator.getInstance().calculateHash(modVal);
|
||||
}
|
||||
|
||||
/****
|
||||
public static void main(String args[]) {
|
||||
Hash k1 = new Hash();
|
||||
byte k1d[] = new byte[Hash.HASH_LENGTH];
|
||||
RandomSource.getInstance().nextBytes(k1d);
|
||||
k1.setData(k1d);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
System.out.println("K1: " + k1);
|
||||
Hash k1m = RoutingKeyGenerator.getInstance().getRoutingKey(k1);
|
||||
System.out.println("MOD: " + new String(RoutingKeyGenerator.getInstance().getModData()));
|
||||
System.out.println("K1M: " + k1m);
|
||||
}
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (Throwable t) { // nop
|
||||
}
|
||||
}
|
||||
****/
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package net.i2p.data.router;
|
||||
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.PrivateKeyFile;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
|
||||
/**
|
||||
* Same format as super, simply adds a method to
|
||||
* treat it as a RouterIdentity instead of a Destination.
|
||||
*
|
||||
* @since 0.9.16
|
||||
*/
|
||||
public class RouterPrivateKeyFile extends PrivateKeyFile {
|
||||
|
||||
public RouterPrivateKeyFile(File file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read it in from the file.
|
||||
* Also sets the local privKey and signingPrivKey.
|
||||
*/
|
||||
public RouterIdentity getRouterIdentity() throws IOException, DataFormatException {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new BufferedInputStream(new FileInputStream(this.file));
|
||||
RouterIdentity ri = new RouterIdentity();
|
||||
ri.readBytes(in);
|
||||
privKey = new PrivateKey();
|
||||
privKey.readBytes(in);
|
||||
SigType type = ri.getSigningPublicKey().getType();
|
||||
if (type == null)
|
||||
throw new DataFormatException("Unknown sig type");
|
||||
signingPrivKey = new SigningPrivateKey(type);
|
||||
signingPrivKey.readBytes(in);
|
||||
|
||||
// set it a Destination, so we may call validateKeyPairs()
|
||||
// or other methods
|
||||
dest = new Destination();
|
||||
dest.setPublicKey(ri.getPublicKey());
|
||||
dest.setSigningPublicKey(ri.getSigningPublicKey());
|
||||
dest.setCertificate(ri.getCertificate());
|
||||
dest.setPadding(ri.getPadding());
|
||||
|
||||
return ri;
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
79
router/java/src/net/i2p/data/router/SortHelper.java
Normal file
@ -0,0 +1,79 @@
|
||||
package net.i2p.data.router;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2003 and released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* your children, but it might. Use at your own risk.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.data.DataStructure;
|
||||
|
||||
/**
|
||||
* The sorting of addresses in RIs
|
||||
*
|
||||
* @since 0.9.16 moved from DataHelper
|
||||
*/
|
||||
class SortHelper {
|
||||
|
||||
/**
|
||||
* Sort based on the Hash of the DataStructure.
|
||||
* Warning - relatively slow.
|
||||
* WARNING - this sort order must be consistent network-wide, so while the order is arbitrary,
|
||||
* it cannot be changed.
|
||||
* Why? Just because it has to be consistent so signing will work.
|
||||
* How to spec as returning the same type as the param?
|
||||
* DEPRECATED - Only used by RouterInfo.
|
||||
*
|
||||
* @return a new list
|
||||
*/
|
||||
public static List<? extends DataStructure> sortStructures(Collection<? extends DataStructure> dataStructures) {
|
||||
if (dataStructures == null) return Collections.emptyList();
|
||||
|
||||
// This used to use Hash.toString(), which is insane, since a change to toString()
|
||||
// would break the whole network. Now use Hash.toBase64().
|
||||
// Note that the Base64 sort order is NOT the same as the raw byte sort order,
|
||||
// despite what you may read elsewhere.
|
||||
|
||||
//ArrayList<DataStructure> rv = new ArrayList(dataStructures.size());
|
||||
//TreeMap<String, DataStructure> tm = new TreeMap();
|
||||
//for (DataStructure struct : dataStructures) {
|
||||
// tm.put(struct.calculateHash().toString(), struct);
|
||||
//}
|
||||
//for (DataStructure struct : tm.values()) {
|
||||
// rv.add(struct);
|
||||
//}
|
||||
ArrayList<DataStructure> rv = new ArrayList<DataStructure>(dataStructures);
|
||||
sortStructureList(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* See above.
|
||||
* DEPRECATED - Only used by RouterInfo.
|
||||
*
|
||||
* @since 0.9
|
||||
*/
|
||||
static void sortStructureList(List<? extends DataStructure> dataStructures) {
|
||||
Collections.sort(dataStructures, new DataStructureComparator());
|
||||
}
|
||||
|
||||
/**
|
||||
* See sortStructures() comments.
|
||||
* @since 0.8.3
|
||||
*/
|
||||
private static class DataStructureComparator implements Comparator<DataStructure>, Serializable {
|
||||
public int compare(DataStructure l, DataStructure r) {
|
||||
return l.calculateHash().toBase64().compareTo(r.calculateHash().toBase64());
|
||||
}
|
||||
}
|
||||
}
|
7
router/java/src/net/i2p/data/router/package.html
Normal file
@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
Classes formerly in net.i2p.data but moved here as they are only used by the router.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
@ -28,8 +28,8 @@ import java.util.TreeSet;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import net.i2p.util.Addresses;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
|
@ -13,7 +13,7 @@ import java.io.Writer;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
|
||||
/**
|
||||
* Manages the communication subsystem between peers, including connections,
|
||||
|
@ -9,7 +9,7 @@ package net.i2p.router;
|
||||
*/
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterIdentity;
|
||||
import net.i2p.data.router.RouterIdentity;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
|
||||
/**
|
||||
|
@ -14,7 +14,7 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterIdentity;
|
||||
import net.i2p.data.router.RouterIdentity;
|
||||
import net.i2p.data.i2np.DatabaseLookupMessage;
|
||||
import net.i2p.data.i2np.DatabaseSearchReplyMessage;
|
||||
import net.i2p.data.i2np.DeliveryStatusMessage;
|
||||
|
@ -18,6 +18,7 @@ import java.io.OutputStream;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataStructure;
|
||||
import net.i2p.data.Destination;
|
||||
@ -26,6 +27,7 @@ import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.router.startup.CreateRouterInfoJob;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
@ -47,10 +49,10 @@ public class KeyManager {
|
||||
|
||||
public final static String PROP_KEYDIR = "router.keyBackupDir";
|
||||
public final static String DEFAULT_KEYDIR = "keyBackup";
|
||||
private final static String KEYFILE_PRIVATE_ENC = "privateEncryption.key";
|
||||
private final static String KEYFILE_PUBLIC_ENC = "publicEncryption.key";
|
||||
private final static String KEYFILE_PRIVATE_SIGNING = "privateSigning.key";
|
||||
private final static String KEYFILE_PUBLIC_SIGNING = "publicSigning.key";
|
||||
public final static String KEYFILE_PRIVATE_ENC = "privateEncryption.key";
|
||||
public final static String KEYFILE_PUBLIC_ENC = "publicEncryption.key";
|
||||
public final static String KEYFILE_PRIVATE_SIGNING = "privateSigning.key";
|
||||
public final static String KEYFILE_PUBLIC_SIGNING = "publicSigning.key";
|
||||
|
||||
public KeyManager(RouterContext context) {
|
||||
_context = context;
|
||||
@ -151,8 +153,9 @@ public class KeyManager {
|
||||
private void syncKeys(File keyDir) {
|
||||
syncPrivateKey(keyDir);
|
||||
syncPublicKey(keyDir);
|
||||
syncSigningKey(keyDir);
|
||||
syncVerificationKey(keyDir);
|
||||
SigType type = CreateRouterInfoJob.getSigTypeConfig(getContext());
|
||||
syncSigningKey(keyDir, type);
|
||||
syncVerificationKey(keyDir, type);
|
||||
}
|
||||
|
||||
private void syncPrivateKey(File keyDir) {
|
||||
@ -181,27 +184,33 @@ public class KeyManager {
|
||||
_publicKey = (PublicKey) readin;
|
||||
}
|
||||
|
||||
private void syncSigningKey(File keyDir) {
|
||||
/**
|
||||
* @param type the SigType to expect on read-in, ignored on write
|
||||
*/
|
||||
private void syncSigningKey(File keyDir, SigType type) {
|
||||
DataStructure ds;
|
||||
File keyFile = new File(keyDir, KEYFILE_PRIVATE_SIGNING);
|
||||
boolean exists = (_signingPrivateKey != null);
|
||||
if (exists)
|
||||
ds = _signingPrivateKey;
|
||||
else
|
||||
ds = new SigningPrivateKey();
|
||||
ds = new SigningPrivateKey(type);
|
||||
DataStructure readin = syncKey(keyFile, ds, exists);
|
||||
if (readin != null && !exists)
|
||||
_signingPrivateKey = (SigningPrivateKey) readin;
|
||||
}
|
||||
|
||||
private void syncVerificationKey(File keyDir) {
|
||||
/**
|
||||
* @param type the SigType to expect on read-in, ignored on write
|
||||
*/
|
||||
private void syncVerificationKey(File keyDir, SigType type) {
|
||||
DataStructure ds;
|
||||
File keyFile = new File(keyDir, KEYFILE_PUBLIC_SIGNING);
|
||||
boolean exists = (_signingPublicKey != null);
|
||||
if (exists)
|
||||
ds = _signingPublicKey;
|
||||
else
|
||||
ds = new SigningPublicKey();
|
||||
ds = new SigningPublicKey(type);
|
||||
DataStructure readin = syncKey(keyFile, ds, exists);
|
||||
if (readin != null && !exists)
|
||||
_signingPublicKey = (SigningPublicKey) readin;
|
||||
|
@ -40,7 +40,7 @@ public class LeaseSetKeys {
|
||||
/**
|
||||
* Key with which a LeaseSet can be revoked (by republishing it with no Leases)
|
||||
*
|
||||
* @deprecated unused
|
||||
* Deprecated, unused
|
||||
*/
|
||||
public SigningPrivateKey getRevocationKey() { return _revocationKey; }
|
||||
|
||||
|
@ -10,7 +10,7 @@ import java.util.Scanner;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.Router;
|
||||
|
||||
/**
|
||||
|
@ -14,9 +14,10 @@ import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.DatabaseEntry;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.networkdb.reseed.ReseedChecker;
|
||||
|
||||
/**
|
||||
@ -51,18 +52,51 @@ public abstract class NetworkDatabaseFacade implements Service {
|
||||
public abstract LeaseSet lookupLeaseSetLocally(Hash key);
|
||||
public abstract void lookupRouterInfo(Hash key, Job onFindJob, Job onFailedLookupJob, long timeoutMs);
|
||||
public abstract RouterInfo lookupRouterInfoLocally(Hash key);
|
||||
|
||||
/**
|
||||
* return the leaseSet if another leaseSet already existed at that key
|
||||
* Lookup using the client's tunnels
|
||||
* Succeeds even if LS validation fails due to unsupported sig type
|
||||
*
|
||||
* @param fromLocalDest use these tunnels for the lookup, or null for exploratory
|
||||
* @since 0.9.16
|
||||
*/
|
||||
public abstract void lookupDestination(Hash key, Job onFinishedJob, long timeoutMs, Hash fromLocalDest);
|
||||
|
||||
/**
|
||||
* Lookup locally in netDB and in badDest cache
|
||||
* Succeeds even if LS validation failed due to unsupported sig type
|
||||
*
|
||||
* @since 0.9.16
|
||||
*/
|
||||
public abstract Destination lookupDestinationLocally(Hash key);
|
||||
|
||||
/**
|
||||
* @return the leaseSet if another leaseSet already existed at that key
|
||||
*
|
||||
* @throws IllegalArgumentException if the data is not valid
|
||||
*/
|
||||
public abstract LeaseSet store(Hash key, LeaseSet leaseSet) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* return the routerInfo if another router already existed at that key
|
||||
* @return the routerInfo if another router already existed at that key
|
||||
*
|
||||
* @throws IllegalArgumentException if the data is not valid
|
||||
*/
|
||||
public abstract RouterInfo store(Hash key, RouterInfo routerInfo) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* @return the old entry if it already existed at that key
|
||||
* @throws IllegalArgumentException if the data is not valid
|
||||
* @since 0.9.16
|
||||
*/
|
||||
public DatabaseEntry store(Hash key, DatabaseEntry entry) throws IllegalArgumentException {
|
||||
if (entry.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO)
|
||||
return store(key, (RouterInfo) entry);
|
||||
if (entry.getType() == DatabaseEntry.KEY_TYPE_LEASESET)
|
||||
return store(key, (LeaseSet) entry);
|
||||
throw new IllegalArgumentException("unknown type");
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if the local router is not valid
|
||||
*/
|
||||
@ -101,4 +135,12 @@ public abstract class NetworkDatabaseFacade implements Service {
|
||||
* @since IPv6
|
||||
*/
|
||||
public boolean floodfillEnabled() { return false; };
|
||||
|
||||
/**
|
||||
* Is it permanently negative cached?
|
||||
*
|
||||
* @param key only for Destinations; for RouterIdentities, see Banlist
|
||||
* @since 0.9.16
|
||||
*/
|
||||
public boolean isNegativeCachedForever(Hash key) { return false; }
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.router.util.CDPQEntry;
|
||||
import net.i2p.util.Log;
|
||||
|
@ -70,9 +70,8 @@ public class PersistentKeyRing extends KeyRing {
|
||||
Hash h = e.getKey();
|
||||
buf.append(h.toBase64().substring(0, 6)).append("…");
|
||||
buf.append("<td>");
|
||||
LeaseSet ls = _ctx.netDb().lookupLeaseSetLocally(h);
|
||||
if (ls != null) {
|
||||
Destination dest = ls.getDestination();
|
||||
Destination dest = _ctx.netDb().lookupDestinationLocally(h);
|
||||
if (dest != null) {
|
||||
if (_ctx.clientManager().isLocal(dest)) {
|
||||
TunnelPoolSettings in = _ctx.tunnelManager().getInboundSettings(h);
|
||||
if (in != null && in.getDestinationNickname() != null)
|
||||
|
@ -29,11 +29,12 @@ import net.i2p.data.Certificate;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.i2np.GarlicMessage;
|
||||
import net.i2p.router.message.GarlicMessageHandler;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import net.i2p.router.startup.CreateRouterInfoJob;
|
||||
import net.i2p.router.startup.StartupJob;
|
||||
import net.i2p.router.startup.WorkingDir;
|
||||
import net.i2p.router.tasks.*;
|
||||
@ -98,10 +99,6 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
/** this does not put an 'H' in your routerInfo **/
|
||||
public final static String PROP_HIDDEN_HIDDEN = "router.isHidden";
|
||||
public final static String PROP_DYNAMIC_KEYS = "router.dynamicKeys";
|
||||
public final static String PROP_INFO_FILENAME = "router.info.location";
|
||||
public final static String PROP_INFO_FILENAME_DEFAULT = "router.info";
|
||||
public final static String PROP_KEYS_FILENAME = "router.keys.location";
|
||||
public final static String PROP_KEYS_FILENAME_DEFAULT = "router.keys";
|
||||
public final static String PROP_SHUTDOWN_IN_PROGRESS = "__shutdownInProgress";
|
||||
public final static String DNS_CACHE_TIME = "" + (5*60);
|
||||
private static final String EVENTLOG = "eventlog.txt";
|
||||
@ -673,20 +670,6 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
return _context.commSystem().isInBadCountry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Only called at startup via LoadRouterInfoJob and RebuildRouterInfoJob.
|
||||
* Not called by periodic RepublishLocalRouterInfoJob.
|
||||
* We don't want to change the cert on the fly as it changes the router hash.
|
||||
* RouterInfo.isHidden() checks the capability, but RouterIdentity.isHidden() checks the cert.
|
||||
* There's no reason to ever add a hidden cert?
|
||||
* @return the certificate for a new RouterInfo - probably a null cert.
|
||||
*/
|
||||
public Certificate createCertificate() {
|
||||
if (_context.getBooleanProperty(PROP_HIDDEN))
|
||||
return new Certificate(Certificate.CERTIFICATE_TYPE_HIDDEN, null);
|
||||
return Certificate.NULL_CERT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.3
|
||||
*/
|
||||
@ -698,14 +681,16 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
* Ugly list of files that we need to kill if we are building a new identity
|
||||
*
|
||||
*/
|
||||
private static final String _rebuildFiles[] = new String[] { "router.info",
|
||||
"router.keys",
|
||||
private static final String _rebuildFiles[] = new String[] {
|
||||
CreateRouterInfoJob.INFO_FILENAME,
|
||||
CreateRouterInfoJob.KEYS_FILENAME,
|
||||
CreateRouterInfoJob.KEYS2_FILENAME,
|
||||
"netDb/my.info", // no longer used
|
||||
"connectionTag.keys", // never used?
|
||||
"keyBackup/privateEncryption.key",
|
||||
"keyBackup/privateSigning.key",
|
||||
"keyBackup/publicEncryption.key",
|
||||
"keyBackup/publicSigning.key",
|
||||
KeyManager.DEFAULT_KEYDIR + '/' + KeyManager.KEYFILE_PRIVATE_ENC,
|
||||
KeyManager.DEFAULT_KEYDIR + '/' + KeyManager.KEYFILE_PUBLIC_ENC,
|
||||
KeyManager.DEFAULT_KEYDIR + '/' + KeyManager.KEYFILE_PRIVATE_SIGNING,
|
||||
KeyManager.DEFAULT_KEYDIR + '/' + KeyManager.KEYFILE_PUBLIC_SIGNING,
|
||||
"sessionKeys.dat" // no longer used
|
||||
};
|
||||
|
||||
@ -1085,7 +1070,7 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
return;
|
||||
_eventLog.addEvent(EventLog.CLOCK_SHIFT, Long.toString(delta));
|
||||
// update the routing key modifier
|
||||
_context.routingKeyGenerator().generateDateBasedModData();
|
||||
_context.routerKeyGenerator().generateDateBasedModData();
|
||||
if (_context.commSystem().countActivePeers() <= 0)
|
||||
return;
|
||||
if (delta > 0)
|
||||
|
@ -10,7 +10,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.RoutingKeyGenerator;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.router.RouterKeyGenerator;
|
||||
import net.i2p.internal.InternalClientManager;
|
||||
import net.i2p.router.client.ClientManagerFacadeImpl;
|
||||
import net.i2p.router.crypto.TransientSessionKeyManager;
|
||||
@ -65,6 +67,7 @@ public class RouterContext extends I2PAppContext {
|
||||
//private MessageStateMonitor _messageStateMonitor;
|
||||
private RouterThrottle _throttle;
|
||||
private RouterAppManager _appManager;
|
||||
private RouterKeyGenerator _routingKeyGenerator;
|
||||
private final Set<Runnable> _finalShutdownTasks;
|
||||
// split up big lock on this to avoid deadlocks
|
||||
private volatile boolean _initialized;
|
||||
@ -183,6 +186,7 @@ public class RouterContext extends I2PAppContext {
|
||||
_messageHistory = new MessageHistory(this);
|
||||
_messageRegistry = new OutboundMessageRegistry(this);
|
||||
//_messageStateMonitor = new MessageStateMonitor(this);
|
||||
_routingKeyGenerator = new RouterKeyGenerator(this);
|
||||
if (!getBooleanProperty("i2p.dummyNetDb"))
|
||||
_netDb = new FloodfillNetworkDatabaseFacade(this); // new KademliaNetworkDatabaseFacade(this);
|
||||
else
|
||||
@ -582,4 +586,35 @@ public class RouterContext extends I2PAppContext {
|
||||
_sessionKeyManagerInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine how much do we want to mess with the keys to turn them
|
||||
* into something we can route. This is context specific because we
|
||||
* may want to test out how things react when peers don't agree on
|
||||
* how to skew.
|
||||
*
|
||||
* Returns same thing as routerKeyGenerator()
|
||||
*
|
||||
* @return non-null
|
||||
* @since 0.9.16 Overrides I2PAppContext. Returns non-null in RouterContext and null in I2PAppcontext.
|
||||
*/
|
||||
@Override
|
||||
public RoutingKeyGenerator routingKeyGenerator() {
|
||||
return _routingKeyGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine how much do we want to mess with the keys to turn them
|
||||
* into something we can route. This is context specific because we
|
||||
* may want to test out how things react when peers don't agree on
|
||||
* how to skew.
|
||||
*
|
||||
* Returns same thing as routingKeyGenerator()
|
||||
*
|
||||
* @return non-null
|
||||
* @since 0.9.16
|
||||
*/
|
||||
public RouterKeyGenerator routerKeyGenerator() {
|
||||
return _routingKeyGenerator;
|
||||
}
|
||||
}
|
||||
|