forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p' (head d046bffcd4f94b253e1aa2bfc9a90482974363dd)
to branch 'i2p.i2p.zzz.test2' (head d00c6fd9c9aef6c37218a791a12f2da957181cd2)
This commit is contained in:
@@ -18,7 +18,7 @@ public class CoreVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
|
||||
public final static String VERSION = "0.9.18";
|
||||
public final static String VERSION = "0.9.19";
|
||||
|
||||
/**
|
||||
* For Vuze.
|
||||
|
||||
@@ -939,6 +939,7 @@ public class I2PAppContext {
|
||||
/**
|
||||
* Use instead of SimpleScheduler.getInstance()
|
||||
* @since 0.9 to replace static instance in the class
|
||||
* @deprecated in 0.9.20, use simpleTimer2()
|
||||
*/
|
||||
public SimpleScheduler simpleScheduler() {
|
||||
if (!_simpleSchedulerInitialized)
|
||||
@@ -946,6 +947,9 @@ public class I2PAppContext {
|
||||
return _simpleScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in 0.9.20
|
||||
*/
|
||||
private void initializeSimpleScheduler() {
|
||||
synchronized (_lock18) {
|
||||
if (_simpleScheduler == null)
|
||||
|
||||
@@ -59,9 +59,14 @@ import net.i2p.util.VersionComparator;
|
||||
* Implementation of an I2P session running over TCP. This class is NOT thread safe -
|
||||
* only one thread should send messages at any given time
|
||||
*
|
||||
* Public only for clearCache().
|
||||
* Except for methods defined in I2PSession and I2CPMessageEventListener,
|
||||
* not maintained as a public API, not for external use.
|
||||
* Use I2PClientFactory to get an I2PClient and then createSession().
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessageEventListener {
|
||||
public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessageEventListener {
|
||||
protected final Log _log;
|
||||
/** who we are */
|
||||
private final Destination _myDestination;
|
||||
@@ -1149,6 +1154,13 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 0.9.20 */
|
||||
public static void clearCache() {
|
||||
synchronized (_lookupCache) {
|
||||
_lookupCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking. Waits a max of 10 seconds by default.
|
||||
* See lookupDest with maxWait parameter to change.
|
||||
@@ -1353,7 +1365,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
boolean close = Boolean.parseBoolean(_options.getProperty("i2cp.closeOnIdle"));
|
||||
if (reduce || close) {
|
||||
updateActivity();
|
||||
_context.simpleScheduler().addEvent(new SessionIdleTimer(_context, this, reduce, close), SessionIdleTimer.MINIMUM_TIME);
|
||||
_context.simpleTimer2().addEvent(new SessionIdleTimer(_context, this, reduce, close), SessionIdleTimer.MINIMUM_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,6 @@ class SessionIdleTimer implements SimpleTimer.TimedEvent {
|
||||
} else {
|
||||
nextDelay = _minimumTime - (now - lastActivity);
|
||||
}
|
||||
_context.simpleScheduler().addEvent(this, nextDelay);
|
||||
_context.simpleTimer2().addEvent(this, nextDelay);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,8 @@ public final class I2PDatagramDissector {
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(I2PDatagramDissector.class);
|
||||
log.error("Caught IOException - INCONSISTENT STATE!", e);
|
||||
log.error("Error loading datagram", e);
|
||||
throw new DataFormatException("Error loading datagram", e);
|
||||
//} catch(AssertionError e) {
|
||||
// Log log = I2PAppContext.getGlobalContext().logManager().getLog(I2PDatagramDissector.class);
|
||||
// log.error("Assertion failed!", e);
|
||||
|
||||
@@ -18,11 +18,14 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataFormatException;
|
||||
@@ -860,7 +863,7 @@ public class BlockfileNamingService extends DummyNamingService {
|
||||
iter = sl.find(beginWith);
|
||||
else
|
||||
iter = sl.iterator();
|
||||
Map<String, Destination> rv = new HashMap<String, Destination>();
|
||||
Map<String, Destination> rv = new TreeMap<String, Destination>();
|
||||
for (int i = 0; i < skip && iter.hasNext(); i++) {
|
||||
// don't bother validating here
|
||||
iter.next();
|
||||
@@ -896,6 +899,188 @@ public class BlockfileNamingService extends DummyNamingService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options If non-null and contains the key "list", get
|
||||
* from that list (default "hosts.txt", NOT all lists)
|
||||
* Key "skip": skip that many entries
|
||||
* Key "limit": max number to return
|
||||
* Key "search": return only those matching substring
|
||||
* Key "startsWith": return only those starting with
|
||||
* ("[0-9]" allowed)
|
||||
* Key "beginWith": start here in the iteration
|
||||
* Don't use both startsWith and beginWith.
|
||||
* Search, startsWith, and beginWith values must be lower case.
|
||||
* @since 0.9.20
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getBase64Entries(Properties options) {
|
||||
String listname = FALLBACK_LIST;
|
||||
String search = null;
|
||||
String startsWith = null;
|
||||
String beginWith = null;
|
||||
int limit = Integer.MAX_VALUE;
|
||||
int skip = 0;
|
||||
if (options != null) {
|
||||
String ln = options.getProperty("list");
|
||||
if (ln != null)
|
||||
listname = ln;
|
||||
search = options.getProperty("search");
|
||||
startsWith = options.getProperty("startsWith");
|
||||
beginWith = options.getProperty("beginWith");
|
||||
if (beginWith == null && startsWith != null) {
|
||||
if (startsWith.equals("[0-9]"))
|
||||
beginWith = "0";
|
||||
else
|
||||
beginWith = startsWith;
|
||||
}
|
||||
String lim = options.getProperty("limit");
|
||||
try {
|
||||
limit = Integer.parseInt(lim);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
String sk = options.getProperty("skip");
|
||||
try {
|
||||
skip = Integer.parseInt(sk);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
synchronized(_bf) {
|
||||
if (_isClosed)
|
||||
return Collections.emptyMap();
|
||||
try {
|
||||
SkipList sl = _bf.getIndex(listname, _stringSerializer, _destSerializer);
|
||||
if (sl == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No skiplist found for lookup in " + listname);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
SkipIterator iter;
|
||||
if (beginWith != null)
|
||||
iter = sl.find(beginWith);
|
||||
else
|
||||
iter = sl.iterator();
|
||||
Map<String, String> rv = new TreeMap<String, String>();
|
||||
for (int i = 0; i < skip && iter.hasNext(); i++) {
|
||||
// don't bother validating here
|
||||
iter.next();
|
||||
}
|
||||
for (int i = 0; i < limit && iter.hasNext(); ) {
|
||||
String key = (String) iter.nextKey();
|
||||
if (startsWith != null) {
|
||||
if (startsWith.equals("[0-9]")) {
|
||||
if (key.charAt(0) > '9')
|
||||
break;
|
||||
} else if (!key.startsWith(startsWith)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
DestEntry de = (DestEntry) iter.next();
|
||||
if (!validate(key, de, listname))
|
||||
continue;
|
||||
if (search != null && key.indexOf(search) < 0)
|
||||
continue;
|
||||
rv.put(key, de.dest.toBase64());
|
||||
i++;
|
||||
}
|
||||
return rv;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("DB lookup error", ioe);
|
||||
return Collections.emptyMap();
|
||||
} catch (RuntimeException re) {
|
||||
_log.error("DB lookup error", re);
|
||||
return Collections.emptyMap();
|
||||
} finally {
|
||||
deleteInvalid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options If non-null and contains the key "list", get
|
||||
* from that list (default "hosts.txt", NOT all lists)
|
||||
* Key "skip": skip that many entries
|
||||
* Key "limit": max number to return
|
||||
* Key "search": return only those matching substring
|
||||
* Key "startsWith": return only those starting with
|
||||
* ("[0-9]" allowed)
|
||||
* Key "beginWith": start here in the iteration
|
||||
* Don't use both startsWith and beginWith.
|
||||
* Search, startsWith, and beginWith values must be lower case.
|
||||
* @since 0.9.20
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getNames(Properties options) {
|
||||
String listname = FALLBACK_LIST;
|
||||
String search = null;
|
||||
String startsWith = null;
|
||||
String beginWith = null;
|
||||
int limit = Integer.MAX_VALUE;
|
||||
int skip = 0;
|
||||
if (options != null) {
|
||||
String ln = options.getProperty("list");
|
||||
if (ln != null)
|
||||
listname = ln;
|
||||
search = options.getProperty("search");
|
||||
startsWith = options.getProperty("startsWith");
|
||||
beginWith = options.getProperty("beginWith");
|
||||
if (beginWith == null && startsWith != null) {
|
||||
if (startsWith.equals("[0-9]"))
|
||||
beginWith = "0";
|
||||
else
|
||||
beginWith = startsWith;
|
||||
}
|
||||
String lim = options.getProperty("limit");
|
||||
try {
|
||||
limit = Integer.parseInt(lim);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
String sk = options.getProperty("skip");
|
||||
try {
|
||||
skip = Integer.parseInt(sk);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
synchronized(_bf) {
|
||||
if (_isClosed)
|
||||
return Collections.emptySet();
|
||||
try {
|
||||
SkipList sl = _bf.getIndex(listname, _stringSerializer, _destSerializer);
|
||||
if (sl == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No skiplist found for lookup in " + listname);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
SkipIterator iter;
|
||||
if (beginWith != null)
|
||||
iter = sl.find(beginWith);
|
||||
else
|
||||
iter = sl.iterator();
|
||||
Set<String> rv = new HashSet<String>();
|
||||
for (int i = 0; i < skip && iter.hasNext(); i++) {
|
||||
iter.next();
|
||||
}
|
||||
for (int i = 0; i < limit && iter.hasNext(); ) {
|
||||
String key = (String) iter.nextKey();
|
||||
if (startsWith != null) {
|
||||
if (startsWith.equals("[0-9]")) {
|
||||
if (key.charAt(0) > '9')
|
||||
break;
|
||||
} else if (!key.startsWith(startsWith)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (search != null && key.indexOf(search) < 0)
|
||||
continue;
|
||||
rv.add(key);
|
||||
i++;
|
||||
}
|
||||
return rv;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("DB lookup error", ioe);
|
||||
return Collections.emptySet();
|
||||
} catch (RuntimeException re) {
|
||||
_log.error("DB lookup error", re);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options ignored
|
||||
* @since 0.8.9
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.i2p.client.naming;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -179,6 +181,19 @@ public class MetaNamingService extends DummyNamingService {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* All services aggregated
|
||||
* @since 0.9.20
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getBase64Entries(Properties options) {
|
||||
Map<String, String> rv = new HashMap<String, String>();
|
||||
for (NamingService ns : _services) {
|
||||
rv.putAll(ns.getBase64Entries(options));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* All services aggregated
|
||||
*/
|
||||
@@ -191,6 +206,17 @@ public class MetaNamingService extends DummyNamingService {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* All services aggregated.
|
||||
* Duplicates not removed (for efficiency)
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void export(Writer out, Properties options) throws IOException {
|
||||
for (NamingService ns : _services) {
|
||||
export(out, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All services aggregated
|
||||
*/
|
||||
|
||||
@@ -7,12 +7,16 @@
|
||||
*/
|
||||
package net.i2p.client.naming;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@@ -235,14 +239,83 @@ public abstract class NamingService {
|
||||
* Warning - This will bring the whole database into memory
|
||||
* if options is null, empty, or unsupported, use with caution.
|
||||
*
|
||||
* This implementation calls getEntries(options) and returns a SortedMap.
|
||||
* Subclasses should override if they store base64 natively.
|
||||
*
|
||||
* @param options NamingService-specific, can be null
|
||||
* @return all mappings (matching the options if non-null)
|
||||
* or empty Map if none;
|
||||
* Returned Map is not necessarily sorted, implementation dependent
|
||||
* @since 0.8.7
|
||||
* @since 0.8.7, implemented in 0.9.20
|
||||
*/
|
||||
public Map<String, String> getBase64Entries(Properties options) {
|
||||
return Collections.emptyMap();
|
||||
Map<String, Destination> entries = getEntries(options);
|
||||
if (entries.size() <= 0)
|
||||
return Collections.emptyMap();
|
||||
Map<String, String> rv = new TreeMap<String, String>();
|
||||
for (Map.Entry<String, Destination> e : entries.entrySet()) {
|
||||
rv.put(e.getKey(), e.getValue().toBase64());
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export in a hosts.txt format.
|
||||
* Output is not necessarily sorted, implementation dependent.
|
||||
* Output may or may not contain comment lines, implementation dependent.
|
||||
* Caller must close writer.
|
||||
*
|
||||
* This implementation calls getBase64Entries().
|
||||
* Subclasses should override if they store in a hosts.txt format natively.
|
||||
*
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void export(Writer out) throws IOException {
|
||||
export(out, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export in a hosts.txt format.
|
||||
* Output is not necessarily sorted, implementation dependent.
|
||||
* Output may or may not contain comment lines, implementation dependent.
|
||||
* Caller must close writer.
|
||||
*
|
||||
* This implementation calls getBase64Entries(options).
|
||||
* Subclasses should override if they store in a hosts.txt format natively.
|
||||
*
|
||||
* @param options NamingService-specific, can be null
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void export(Writer out, Properties options) throws IOException {
|
||||
Map<String, String> entries = getBase64Entries(options);
|
||||
out.write("# Address book: ");
|
||||
out.write(getName());
|
||||
if (options != null) {
|
||||
String list = options.getProperty("list");
|
||||
if (list != null)
|
||||
out.write(" (" + list + ')');
|
||||
}
|
||||
final String nl = System.getProperty("line.separator", "\n");
|
||||
out.write(nl);
|
||||
int sz = entries.size();
|
||||
if (sz <= 0) {
|
||||
out.write("# No entries");
|
||||
out.write(nl);
|
||||
return;
|
||||
}
|
||||
out.write("# Exported: ");
|
||||
out.write((new Date()).toString());
|
||||
out.write(nl);
|
||||
if (sz > 1) {
|
||||
out.write("# " + sz + " entries");
|
||||
out.write(nl);
|
||||
}
|
||||
for (Map.Entry<String, String> e : entries.entrySet()) {
|
||||
out.write(e.getKey());
|
||||
out.write('=');
|
||||
out.write(e.getValue());
|
||||
out.write(nl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,9 @@ import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@@ -364,6 +366,103 @@ public class SingleFileNamingService extends NamingService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden since we store base64 natively.
|
||||
*
|
||||
* @param options As follows:
|
||||
* Key "search": return only those matching substring
|
||||
* Key "startsWith": return only those starting with
|
||||
* ("[0-9]" allowed)
|
||||
* @return all mappings (matching the options if non-null)
|
||||
* or empty Map if none.
|
||||
* Returned Map is not sorted.
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public Map<String, String> getBase64Entries(Properties options) {
|
||||
if (!_file.exists())
|
||||
return Collections.emptyMap();
|
||||
String searchOpt = null;
|
||||
String startsWith = null;
|
||||
if (options != null) {
|
||||
searchOpt = options.getProperty("search");
|
||||
startsWith = options.getProperty("startsWith");
|
||||
}
|
||||
BufferedReader in = null;
|
||||
getReadLock();
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(new FileInputStream(_file), "UTF-8"), 16*1024);
|
||||
String line = null;
|
||||
Map<String, String> rv = new HashMap<String, String>();
|
||||
while ( (line = in.readLine()) != null) {
|
||||
if (line.length() <= 0)
|
||||
continue;
|
||||
if (startsWith != null) {
|
||||
if (startsWith.equals("[0-9]")) {
|
||||
if (line.charAt(0) < '0' || line.charAt(0) > '9')
|
||||
continue;
|
||||
} else if (!line.startsWith(startsWith)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (line.startsWith("#"))
|
||||
continue;
|
||||
if (line.indexOf('#') > 0) // trim off any end of line comment
|
||||
line = line.substring(0, line.indexOf('#')).trim();
|
||||
int split = line.indexOf('=');
|
||||
if (split <= 0)
|
||||
continue;
|
||||
String key = line.substring(0, split);
|
||||
if (searchOpt != null && key.indexOf(searchOpt) < 0)
|
||||
continue;
|
||||
String b64 = line.substring(split+1); //.trim() ??????????????
|
||||
if (b64.length() < 387)
|
||||
continue;
|
||||
rv.put(key, b64);
|
||||
}
|
||||
if (searchOpt == null && startsWith == null) {
|
||||
_lastWrite = _file.lastModified();
|
||||
_size = rv.size();
|
||||
}
|
||||
return rv;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("getEntries error", ioe);
|
||||
return Collections.emptyMap();
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
releaseReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden for efficiency.
|
||||
* Output is not sorted.
|
||||
*
|
||||
* @param options ignored
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void export(Writer out, Properties options) throws IOException {
|
||||
out.write("# Address book: ");
|
||||
out.write(getName());
|
||||
final String nl = System.getProperty("line.separator", "\n");
|
||||
out.write(nl);
|
||||
out.write("# Exported: ");
|
||||
out.write((new Date()).toString());
|
||||
out.write(nl);
|
||||
BufferedReader in = null;
|
||||
getReadLock();
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(new FileInputStream(_file), "UTF-8"), 16*1024);
|
||||
String line = null;
|
||||
while ( (line = in.readLine()) != null) {
|
||||
out.write(line);
|
||||
out.write(nl);
|
||||
}
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
releaseReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options ignored
|
||||
* @return all known host names, unsorted
|
||||
|
||||
@@ -53,6 +53,8 @@ public class CertUtil {
|
||||
}
|
||||
wr.println("-----END CERTIFICATE-----");
|
||||
wr.flush();
|
||||
if (wr.checkError())
|
||||
throw new IOException("Failed write to " + file);
|
||||
return true;
|
||||
} catch (CertificateEncodingException cee) {
|
||||
error("Error writing X509 Certificate " + file.getAbsolutePath(), cee);
|
||||
|
||||
@@ -32,6 +32,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
// Debugging methods and variables
|
||||
//...........................................................................
|
||||
|
||||
/****
|
||||
private static final String _NAME = "Rijndael_Algorithm";
|
||||
private static final boolean _IN = true, _OUT = false;
|
||||
|
||||
@@ -53,6 +54,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
private static void trace(String s) {
|
||||
if (_TRACE) _err.println("<=> " + _NAME + "." + s);
|
||||
}
|
||||
****/
|
||||
|
||||
// Constants and variables
|
||||
//...........................................................................
|
||||
@@ -89,11 +91,13 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
//...........................................................................
|
||||
|
||||
static {
|
||||
/****
|
||||
if (_RDEBUG && _debuglevel > 6) {
|
||||
System.out.println("Algorithm Name: Rijndael ver 0.1");
|
||||
System.out.println("Electronic Codebook (ECB) Mode");
|
||||
System.out.println();
|
||||
}
|
||||
****/
|
||||
int ROOT = 0x11B;
|
||||
int i, j = 0;
|
||||
|
||||
@@ -383,7 +387,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
* @param sessionKey The session key to use for encryption.
|
||||
*/
|
||||
public static final void blockEncrypt(byte[] in, byte[] result, int inOffset, int outOffset, Object sessionKey) {
|
||||
if (_RDEBUG) trace(_IN, "blockEncrypt(" + in + ", " + inOffset + ", " + sessionKey + ")");
|
||||
//if (_RDEBUG) trace(_IN, "blockEncrypt(" + in + ", " + inOffset + ", " + sessionKey + ")");
|
||||
int[][] Ke = (int[][]) ((Object[]) sessionKey)[0]; // extract encryption round keys
|
||||
int ROUNDS = Ke.length - 1;
|
||||
int[] Ker = Ke[0];
|
||||
@@ -409,9 +413,11 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
t1 = a1;
|
||||
t2 = a2;
|
||||
t3 = a3;
|
||||
/****
|
||||
if (_RDEBUG && _debuglevel > 6)
|
||||
System.out.println("CT" + r + "=" + intToString(t0) + intToString(t1) + intToString(t2)
|
||||
+ intToString(t3));
|
||||
****/
|
||||
}
|
||||
|
||||
// last round is special
|
||||
@@ -436,11 +442,13 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
result[outOffset++] = (byte) (_S[(t0 >>> 16) & 0xFF] ^ (tt >>> 16));
|
||||
result[outOffset++] = (byte) (_S[(t1 >>> 8) & 0xFF] ^ (tt >>> 8));
|
||||
result[outOffset++] = (byte) (_S[t2 & 0xFF] ^ tt);
|
||||
/****
|
||||
if (_RDEBUG && _debuglevel > 6) {
|
||||
System.out.println("CT=" + toString(result));
|
||||
System.out.println();
|
||||
}
|
||||
if (_RDEBUG) trace(_OUT, "blockEncrypt()");
|
||||
****/
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,7 +466,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
+ " result.len=" + result.length + " result.offset=" + outOffset);
|
||||
if (in.length - inOffset <= 15)
|
||||
throw new IllegalArgumentException("data too small: " + in.length + " inOffset: " + inOffset);
|
||||
if (_RDEBUG) trace(_IN, "blockDecrypt(" + in + ", " + inOffset + ", " + sessionKey + ")");
|
||||
//if (_RDEBUG) trace(_IN, "blockDecrypt(" + in + ", " + inOffset + ", " + sessionKey + ")");
|
||||
int[][] Kd = (int[][]) ((Object[]) sessionKey)[1]; // extract decryption round keys
|
||||
int ROUNDS = Kd.length - 1;
|
||||
int[] Kdr = Kd[0];
|
||||
@@ -484,9 +492,11 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
t1 = a1;
|
||||
t2 = a2;
|
||||
t3 = a3;
|
||||
/****
|
||||
if (_RDEBUG && _debuglevel > 6)
|
||||
System.out.println("PT" + r + "=" + intToString(t0) + intToString(t1) + intToString(t2)
|
||||
+ intToString(t3));
|
||||
****/
|
||||
}
|
||||
|
||||
// last round is special
|
||||
@@ -511,11 +521,13 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
result[outOffset++] = (byte) (_Si[(t2 >>> 16) & 0xFF] ^ (tt >>> 16));
|
||||
result[outOffset++] = (byte) (_Si[(t1 >>> 8) & 0xFF] ^ (tt >>> 8));
|
||||
result[outOffset++] = (byte) (_Si[t0 & 0xFF] ^ tt);
|
||||
/****
|
||||
if (_RDEBUG && _debuglevel > 6) {
|
||||
System.out.println("PT=" + toString(result));
|
||||
System.out.println();
|
||||
}
|
||||
if (_RDEBUG) trace(_OUT, "blockDecrypt()");
|
||||
****/
|
||||
}
|
||||
|
||||
/** A basic symmetric encryption/decryption test. */
|
||||
@@ -544,7 +556,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
return makeKey(k, blockSize, null);
|
||||
}
|
||||
public static final/* synchronized */Object makeKey(byte[] k, int blockSize, CryptixAESKeyCache.KeyCacheEntry keyData) throws InvalidKeyException {
|
||||
if (_RDEBUG) trace(_IN, "makeKey(" + k + ", " + blockSize + ")");
|
||||
//if (_RDEBUG) trace(_IN, "makeKey(" + k + ", " + blockSize + ")");
|
||||
if (k == null) throw new InvalidKeyException("Empty key");
|
||||
if (!(k.length == 16 || k.length == 24 || k.length == 32))
|
||||
throw new InvalidKeyException("Incorrect key length");
|
||||
@@ -629,7 +641,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
sessionKey = new Object[] { Ke, Kd};
|
||||
else
|
||||
sessionKey = keyData.key;
|
||||
if (_RDEBUG) trace(_OUT, "makeKey()");
|
||||
//if (_RDEBUG) trace(_OUT, "makeKey()");
|
||||
return sessionKey;
|
||||
}
|
||||
|
||||
@@ -647,7 +659,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
blockEncrypt(in, result, inOffset, outOffset, sessionKey);
|
||||
return;
|
||||
}
|
||||
if (_RDEBUG) trace(_IN, "blockEncrypt(" + in + ", " + inOffset + ", " + sessionKey + ", " + blockSize + ")");
|
||||
//if (_RDEBUG) trace(_IN, "blockEncrypt(" + in + ", " + inOffset + ", " + sessionKey + ", " + blockSize + ")");
|
||||
Object[] sKey = (Object[]) sessionKey; // extract encryption round keys
|
||||
int[][] Ke = (int[][]) sKey[0];
|
||||
|
||||
@@ -673,7 +685,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
^ _T3[(t[(i + s2) % BC] >>> 8) & 0xFF] ^ _T4[t[(i + s3) % BC] & 0xFF])
|
||||
^ Ke[r][i];
|
||||
System.arraycopy(a, 0, t, 0, BC);
|
||||
if (_RDEBUG && _debuglevel > 6) System.out.println("CT" + r + "=" + toString(t));
|
||||
//if (_RDEBUG && _debuglevel > 6) System.out.println("CT" + r + "=" + toString(t));
|
||||
}
|
||||
for (i = 0; i < BC; i++) { // last round is special
|
||||
tt = Ke[ROUNDS][i];
|
||||
@@ -682,11 +694,13 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
result[j++] = (byte) (_S[(t[(i + s2) % BC] >>> 8) & 0xFF] ^ (tt >>> 8));
|
||||
result[j++] = (byte) (_S[t[(i + s3) % BC] & 0xFF] ^ tt);
|
||||
}
|
||||
/****
|
||||
if (_RDEBUG && _debuglevel > 6) {
|
||||
System.out.println("CT=" + toString(result));
|
||||
System.out.println();
|
||||
}
|
||||
if (_RDEBUG) trace(_OUT, "blockEncrypt()");
|
||||
****/
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -704,7 +718,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
return;
|
||||
}
|
||||
|
||||
if (_RDEBUG) trace(_IN, "blockDecrypt(" + in + ", " + inOffset + ", " + sessionKey + ", " + blockSize + ")");
|
||||
//if (_RDEBUG) trace(_IN, "blockDecrypt(" + in + ", " + inOffset + ", " + sessionKey + ", " + blockSize + ")");
|
||||
Object[] sKey = (Object[]) sessionKey; // extract decryption round keys
|
||||
int[][] Kd = (int[][]) sKey[1];
|
||||
|
||||
@@ -730,7 +744,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
^ _T7[(t[(i + s2) % BC] >>> 8) & 0xFF] ^ _T8[t[(i + s3) % BC] & 0xFF])
|
||||
^ Kd[r][i];
|
||||
System.arraycopy(a, 0, t, 0, BC);
|
||||
if (_RDEBUG && _debuglevel > 6) System.out.println("PT" + r + "=" + toString(t));
|
||||
//if (_RDEBUG && _debuglevel > 6) System.out.println("PT" + r + "=" + toString(t));
|
||||
}
|
||||
for (i = 0; i < BC; i++) { // last round is special
|
||||
tt = Kd[ROUNDS][i];
|
||||
@@ -739,11 +753,13 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
result[j++] = (byte) (_Si[(t[(i + s2) % BC] >>> 8) & 0xFF] ^ (tt >>> 8));
|
||||
result[j++] = (byte) (_Si[t[(i + s3) % BC] & 0xFF] ^ tt);
|
||||
}
|
||||
/****
|
||||
if (_RDEBUG && _debuglevel > 6) {
|
||||
System.out.println("PT=" + toString(result));
|
||||
System.out.println();
|
||||
}
|
||||
if (_RDEBUG) trace(_OUT, "blockDecrypt()");
|
||||
****/
|
||||
}
|
||||
|
||||
/** A basic symmetric encryption/decryption test for a given key size. */
|
||||
|
||||
@@ -77,8 +77,9 @@ public final class SHA256Generator {
|
||||
digest.digest(out, outOffset, Hash.HASH_LENGTH);
|
||||
} catch (DigestException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
release(digest);
|
||||
}
|
||||
release(digest);
|
||||
}
|
||||
|
||||
private MessageDigest acquire() {
|
||||
|
||||
@@ -529,6 +529,11 @@ public class DataHelper {
|
||||
out.flush();
|
||||
fos.getFD().sync();
|
||||
out.close();
|
||||
if (out.checkError()) {
|
||||
out = null;
|
||||
tmpFile.delete();
|
||||
throw new IOException("Failed to write properties to " + tmpFile);
|
||||
}
|
||||
out = null;
|
||||
if (!FileUtil.rename(tmpFile, file))
|
||||
throw new IOException("Failed rename from " + tmpFile + " to " + file);
|
||||
|
||||
@@ -41,7 +41,7 @@ import net.i2p.util.SecureFileOutputStream;
|
||||
* - Certificate if length != 0
|
||||
* - Private key (256 bytes)
|
||||
* - Signing Private key (20 bytes, or length specified by key certificate)
|
||||
* Total 663 bytes
|
||||
* Total: 663 or more bytes
|
||||
*</pre>
|
||||
*
|
||||
* @author welterde, zzz
|
||||
|
||||
@@ -96,7 +96,7 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
|
||||
int size = 2 + 4 + 4 + _payload.getSize();
|
||||
try {
|
||||
DataHelper.writeLong(out, 4, size);
|
||||
DataHelper.writeLong(out, 1, getType());
|
||||
DataHelper.writeLong(out, 1, MESSAGE_TYPE);
|
||||
DataHelper.writeLong(out, 2, _sessionId);
|
||||
DataHelper.writeLong(out, 4, _messageId);
|
||||
DataHelper.writeLong(out, 4, _payload.getSize());
|
||||
|
||||
@@ -308,7 +308,7 @@ public class MessageStatusMessage extends I2CPMessageImpl {
|
||||
|
||||
try {
|
||||
DataHelper.writeLong(out, 4, len);
|
||||
DataHelper.writeLong(out, 1, getType());
|
||||
DataHelper.writeLong(out, 1, MESSAGE_TYPE);
|
||||
DataHelper.writeLong(out, 2, _sessionId);
|
||||
DataHelper.writeLong(out, 4, _messageId);
|
||||
DataHelper.writeLong(out, 1, _status);
|
||||
|
||||
@@ -76,7 +76,7 @@ public class ReceiveMessageBeginMessage extends I2CPMessageImpl {
|
||||
|
||||
try {
|
||||
DataHelper.writeLong(out, 4, len);
|
||||
DataHelper.writeLong(out, 1, getType());
|
||||
DataHelper.writeLong(out, 1, MESSAGE_TYPE);
|
||||
DataHelper.writeLong(out, 2, _sessionId);
|
||||
DataHelper.writeLong(out, 4, _messageId);
|
||||
} catch (DataFormatException dfe) {
|
||||
|
||||
@@ -74,7 +74,7 @@ public class SendMessageMessage extends I2CPMessageImpl {
|
||||
|
||||
@Override
|
||||
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||
if (true) throw new IllegalStateException("wtf, do not run me");
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -69,7 +69,7 @@ public final class ByteCache {
|
||||
/**
|
||||
* Get a cache responsible for objects of the given size.
|
||||
* Warning, if you store the result in a static field, the cleaners will
|
||||
* not operate after a restart on Android, as the old context's SimpleScheduler will have shut down.
|
||||
* not operate after a restart on Android, as the old context's SimpleTimer2 will have shut down.
|
||||
* TODO tie this to the context or clean up all calls.
|
||||
*
|
||||
* @param cacheSize how large we want the cache to grow
|
||||
@@ -123,7 +123,7 @@ public final class ByteCache {
|
||||
_maxCached = maxCachedEntries;
|
||||
_entrySize = entrySize;
|
||||
_lastOverflow = -1;
|
||||
SimpleScheduler.getInstance().addPeriodicEvent(new Cleanup(), CLEANUP_FREQUENCY + (entrySize % 777)); //stagger
|
||||
SimpleTimer2.getInstance().addPeriodicEvent(new Cleanup(), CLEANUP_FREQUENCY + (entrySize % 777)); //stagger
|
||||
I2PAppContext.getGlobalContext().statManager().createRateStat("byteCache.memory." + entrySize, "Memory usage (B)", "Router", new long[] { 10*60*1000 });
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
@@ -61,6 +62,7 @@ public class EepGet {
|
||||
protected List<String> _extraHeaders;
|
||||
|
||||
protected boolean _keepFetching;
|
||||
// The proxy or the actual site if not proxied. Warning - null when extended by I2PSocketEepGet
|
||||
protected Socket _proxy;
|
||||
protected OutputStream _proxyOut;
|
||||
protected InputStream _proxyIn;
|
||||
@@ -82,9 +84,9 @@ public class EepGet {
|
||||
protected boolean _transferFailed;
|
||||
protected boolean _headersRead;
|
||||
protected boolean _aborted;
|
||||
private long _fetchHeaderTimeout;
|
||||
protected int _fetchHeaderTimeout;
|
||||
private long _fetchEndTime;
|
||||
protected long _fetchInactivityTimeout;
|
||||
protected int _fetchInactivityTimeout;
|
||||
protected int _redirects;
|
||||
protected String _redirectLocation;
|
||||
protected boolean _isGzippedResponse;
|
||||
@@ -96,8 +98,8 @@ public class EepGet {
|
||||
|
||||
/** this will be replaced by the HTTP Proxy if we are using it */
|
||||
protected static final String USER_AGENT = "Wget/1.11.4";
|
||||
protected static final long CONNECT_TIMEOUT = 45*1000;
|
||||
protected static final long INACTIVITY_TIMEOUT = 60*1000;
|
||||
protected static final int CONNECT_TIMEOUT = 45*1000;
|
||||
protected static final int INACTIVITY_TIMEOUT = 60*1000;
|
||||
/** maximum times to try without getting any data at all, even if numRetries is higher @since 0.7.14 */
|
||||
protected static final int MAX_COMPLETE_FAILS = 5;
|
||||
|
||||
@@ -577,9 +579,9 @@ public class EepGet {
|
||||
* @param inactivityTimeout <= 0 for default 60 sec
|
||||
*/
|
||||
public boolean fetch(long fetchHeaderTimeout, long totalTimeout, long inactivityTimeout) {
|
||||
_fetchHeaderTimeout = fetchHeaderTimeout;
|
||||
_fetchHeaderTimeout = (int) Math.min(fetchHeaderTimeout, Integer.MAX_VALUE);
|
||||
_fetchEndTime = (totalTimeout > 0 ? System.currentTimeMillis() + totalTimeout : -1);
|
||||
_fetchInactivityTimeout = inactivityTimeout;
|
||||
_fetchInactivityTimeout = (int) Math.min(inactivityTimeout, Integer.MAX_VALUE);
|
||||
_keepFetching = true;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -677,6 +679,14 @@ public class EepGet {
|
||||
else
|
||||
timeout.setInactivityTimeout(INACTIVITY_TIMEOUT);
|
||||
}
|
||||
// _proxy is null when extended by I2PSocketEepGet
|
||||
if (_proxy != null && !_shouldProxy) {
|
||||
// we only set the soTimeout before the headers if not proxied
|
||||
if (_fetchInactivityTimeout > 0)
|
||||
_proxy.setSoTimeout(_fetchInactivityTimeout);
|
||||
else
|
||||
_proxy.setSoTimeout(INACTIVITY_TIMEOUT);
|
||||
}
|
||||
|
||||
if (_redirectLocation != null) {
|
||||
// we also are here after a 407
|
||||
@@ -906,11 +916,14 @@ public class EepGet {
|
||||
return;
|
||||
case 400: // bad req
|
||||
case 401: // server auth
|
||||
case 402: // payment required
|
||||
case 403: // bad req
|
||||
case 404: // not found
|
||||
case 408: // req timeout
|
||||
case 409: // bad addr helper
|
||||
case 410: // gone
|
||||
case 414: // URI too long
|
||||
case 429: // too many requests
|
||||
case 431: // headers too long
|
||||
case 503: // no outproxy
|
||||
_transferFailed = true;
|
||||
@@ -1193,7 +1206,13 @@ public class EepGet {
|
||||
int port = url.getPort();
|
||||
if (port == -1)
|
||||
port = 80;
|
||||
_proxy = new Socket(host, port);
|
||||
if (_fetchHeaderTimeout > 0) {
|
||||
_proxy = new Socket();
|
||||
_proxy.setSoTimeout(_fetchHeaderTimeout);
|
||||
_proxy.connect(new InetSocketAddress(host, port), _fetchHeaderTimeout);
|
||||
} else {
|
||||
_proxy = new Socket(host, port);
|
||||
}
|
||||
} else {
|
||||
throw new MalformedURLException("URL is not supported:" + _actualURL);
|
||||
}
|
||||
|
||||
@@ -21,8 +21,44 @@ package net.i2p.util;
|
||||
* ========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Contains code adapted from:
|
||||
* Apache httpcomponents PublicSuffixMatcherLoader.java
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.security.KeyStore;
|
||||
@@ -34,8 +70,13 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLServerSocket;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
@@ -43,14 +84,41 @@ import javax.net.ssl.TrustManagerFactory;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.KeyStoreUtil;
|
||||
|
||||
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
|
||||
import org.apache.http.conn.util.PublicSuffixList;
|
||||
import org.apache.http.conn.util.PublicSuffixListParser;
|
||||
import org.apache.http.conn.util.PublicSuffixMatcher;
|
||||
|
||||
/**
|
||||
* Loads trusted ASCII certs from ~/.i2p/certificates/ and $I2P/certificates/.
|
||||
*
|
||||
* TODO extend SSLSocketFactory
|
||||
*
|
||||
* @author zzz
|
||||
* @since 0.9.9 moved from ../client, original since 0.8.3
|
||||
*/
|
||||
public class I2PSSLSocketFactory {
|
||||
|
||||
private static final String PROP_DISABLE = "i2p.disableSSLHostnameVerification";
|
||||
private static final String PROP_GEOIP_DIR = "geoip.dir";
|
||||
private static final String GEOIP_DIR_DEFAULT = "geoip";
|
||||
private static final String GEOIP_FILE_DEFAULT = "geoip.txt";
|
||||
private static final String COUNTRY_FILE_DEFAULT = "countries.txt";
|
||||
private static final String PUBLIC_SUFFIX_LIST = "public-suffix-list.txt";
|
||||
private static PublicSuffixMatcher DEFAULT_MATCHER;
|
||||
private static boolean _matcherLoaded;
|
||||
// not in countries.txt, but only the public ones, not the private ones
|
||||
private static final String[] DEFAULT_TLDS = {
|
||||
"arpa", "asia", "biz", "cat", "com", "coop",
|
||||
"edu", "gov", "info", "int", "jobs", "mil",
|
||||
"mobi", "museum", "name", "net", "org", "post",
|
||||
"pro", "tel", "travel", "xxx"
|
||||
};
|
||||
// not in countries.txt or public-suffix-list.txt
|
||||
private static final String[] ADDITIONAL_TLDS = {
|
||||
"i2p", "mooo.com", "onion"
|
||||
};
|
||||
|
||||
/**
|
||||
* Unmodifiable.
|
||||
* Public for RouterConsoleRunner.
|
||||
@@ -148,7 +216,9 @@ public class I2PSSLSocketFactory {
|
||||
*/
|
||||
public static final List<String> INCLUDE_CIPHERS = Collections.emptyList();
|
||||
|
||||
/** the "real" factory */
|
||||
private final SSLSocketFactory _factory;
|
||||
private final I2PAppContext _context;
|
||||
|
||||
/**
|
||||
* @param relativeCertPath e.g. "certificates/i2cp"
|
||||
@@ -157,27 +227,230 @@ public class I2PSSLSocketFactory {
|
||||
public I2PSSLSocketFactory(I2PAppContext context, boolean loadSystemCerts, String relativeCertPath)
|
||||
throws GeneralSecurityException {
|
||||
_factory = initSSLContext(context, loadSystemCerts, relativeCertPath);
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a socket to the host.
|
||||
*
|
||||
* A host argument that's an IP address (instead of a host name)
|
||||
* is not recommended, as this will probably fail
|
||||
* SSL certificate validation.
|
||||
*
|
||||
* Hostname validation is skipped for localhost addresses, but you still
|
||||
* must trust the certificate.
|
||||
*
|
||||
*/
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
SSLSocket rv = (SSLSocket) _factory.createSocket(host, port);
|
||||
setProtocolsAndCiphers(rv);
|
||||
verifyHostname(_context, rv, host);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a socket to the host.
|
||||
*
|
||||
* An InetAddress argument created with an IP address (instead of a host name)
|
||||
* is not recommended, as this will perform a reverse DNS lookup to
|
||||
* get the host name for certificate validation, which will probably then fail.
|
||||
*
|
||||
* Hostname validation is skipped for localhost addresses, but you still
|
||||
* must trust the certificate.
|
||||
*
|
||||
* @since 0.9.9
|
||||
*/
|
||||
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||
SSLSocket rv = (SSLSocket) _factory.createSocket(host, port);
|
||||
setProtocolsAndCiphers(rv);
|
||||
String name = host.getHostName();
|
||||
verifyHostname(_context, rv, name);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the hostname
|
||||
*
|
||||
* ref: https://developer.android.com/training/articles/security-ssl.html
|
||||
* ref: http://op-co.de/blog/posts/java_sslsocket_mitm/
|
||||
* ref: http://kevinlocke.name/bits/2012/10/03/ssl-certificate-verification-in-dispatch-and-asynchttpclient/
|
||||
*
|
||||
* @throws SSLException on hostname verification failure
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public static void verifyHostname(I2PAppContext ctx, SSLSocket socket, String host) throws SSLException {
|
||||
Log log = ctx.logManager().getLog(I2PSSLSocketFactory.class);
|
||||
if (ctx.getBooleanProperty(PROP_DISABLE) ||
|
||||
host.equals("localhost") ||
|
||||
host.equals("127.0.0.1") ||
|
||||
host.equals("::1") ||
|
||||
host.equals("0:0:0:0:0:0:0::1")) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("Skipping hostname validation for " + host);
|
||||
return;
|
||||
}
|
||||
HostnameVerifier hv;
|
||||
if (SystemVersion.isAndroid()) {
|
||||
// https://developer.android.com/training/articles/security-ssl.html
|
||||
hv = HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
} else {
|
||||
// haha the above may work for Android but it doesn't in Oracle
|
||||
//
|
||||
// quote http://kevinlocke.name/bits/2012/10/03/ssl-certificate-verification-in-dispatch-and-asynchttpclient/ :
|
||||
// Unlike SSLContext, using the Java default (HttpsURLConnection.getDefaultHostnameVerifier)
|
||||
// is not a viable option because the default HostnameVerifier expects to only be called
|
||||
// in the case that there is a mismatch (and therefore always returns false) while some
|
||||
// of the AsyncHttpClient providers (e.g. Netty, the default) call it on all connections.
|
||||
// in the case that there is a mismatch (and therefore always returns false) while some
|
||||
// To make matters worse, the check is not trivial (consider SAN and wildcard matching)
|
||||
// and is implemented in sun.security.util.HostnameChecker (a Sun internal proprietary API).
|
||||
// This leaves the developer in the position of either depending on an internal API or
|
||||
// finding/copying/creating another implementation of this functionality.
|
||||
//
|
||||
hv = new DefaultHostnameVerifier(getDefaultMatcher(ctx));
|
||||
}
|
||||
SSLSession sess = socket.getSession();
|
||||
// Verify that the certicate hostname is for mail.google.com
|
||||
// This is due to lack of SNI support in the current SSLSocket.
|
||||
if (!hv.verify(host, sess)) {
|
||||
throw new SSLHandshakeException("SSL hostname verify failed, Expected " + host +
|
||||
// throws SSLPeerUnverifiedException
|
||||
//", found " + sess.getPeerPrincipal() +
|
||||
// returns null
|
||||
//", found " + sess.getPeerHost() +
|
||||
// enable logging for DefaultHostnameVerifier to find out the CN and SANs
|
||||
" - set " + PROP_DISABLE +
|
||||
"=true to disable verification (dangerous!)");
|
||||
}
|
||||
// At this point SSLSocket performed certificate verificaiton and
|
||||
// we have performed hostname verification, so it is safe to proceed.
|
||||
}
|
||||
|
||||
/**
|
||||
* From Apache PublicSuffixMatcherLoader.getDefault()
|
||||
*
|
||||
* https://publicsuffix.org/list/effective_tld_names.dat
|
||||
* What does this get us?
|
||||
* Deciding whether to issue or accept an SSL wildcard certificate for *.public.suffix.
|
||||
*
|
||||
* @return null on failure
|
||||
* @since 0.9.20
|
||||
*/
|
||||
private static PublicSuffixMatcher getDefaultMatcher(I2PAppContext ctx) {
|
||||
synchronized (I2PSSLSocketFactory.class) {
|
||||
if (!_matcherLoaded) {
|
||||
String geoDir = ctx.getProperty(PROP_GEOIP_DIR, GEOIP_DIR_DEFAULT);
|
||||
File geoFile = new File(geoDir);
|
||||
if (!geoFile.isAbsolute())
|
||||
geoFile = new File(ctx.getBaseDir(), geoDir);
|
||||
geoFile = new File(geoFile, PUBLIC_SUFFIX_LIST);
|
||||
Log log = ctx.logManager().getLog(I2PSSLSocketFactory.class);
|
||||
if (geoFile.exists()) {
|
||||
try {
|
||||
// we can't use PublicSuffixMatcherLoader.load() here because we
|
||||
// want to add some of our own and a PublicSuffixMatcher's
|
||||
// underlying PublicSuffixList is immutable and inaccessible
|
||||
long begin = System.currentTimeMillis();
|
||||
InputStream in = null;
|
||||
PublicSuffixList list = new PublicSuffixList(Arrays.asList(ADDITIONAL_TLDS),
|
||||
Collections.<String>emptyList());
|
||||
try {
|
||||
in = new FileInputStream(geoFile);
|
||||
PublicSuffixList list2 = new PublicSuffixListParser().parse(
|
||||
new InputStreamReader(in, "UTF-8"));
|
||||
list = merge(list, list2);
|
||||
} finally {
|
||||
try { if (in != null) in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
DEFAULT_MATCHER = new PublicSuffixMatcher(list.getRules(), list.getExceptions());
|
||||
if (log.shouldWarn())
|
||||
log.warn("Loaded " + geoFile + " in " + (System.currentTimeMillis() - begin) +
|
||||
" ms and created list with " + list.getRules().size() + " entries and " +
|
||||
list.getExceptions().size() + " exceptions");
|
||||
} catch (IOException ex) {
|
||||
log.error("Failure loading public suffix list from " + geoFile, ex);
|
||||
// DEFAULT_MATCHER remains null
|
||||
}
|
||||
} else {
|
||||
List<String> list = new ArrayList<String>(320);
|
||||
addCountries(ctx, list);
|
||||
list.addAll(Arrays.asList(DEFAULT_TLDS));
|
||||
list.addAll(Arrays.asList(ADDITIONAL_TLDS));
|
||||
DEFAULT_MATCHER = new PublicSuffixMatcher(list, null);
|
||||
if (log.shouldWarn())
|
||||
log.warn("No public suffix list found at " + geoFile +
|
||||
" - created default with " + list.size() + " entries");
|
||||
}
|
||||
}
|
||||
_matcherLoaded = true;
|
||||
}
|
||||
return DEFAULT_MATCHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two PublicSuffixLists
|
||||
* Have to do this because they are unmodifiable
|
||||
*
|
||||
* @since 0.9.20
|
||||
*/
|
||||
private static PublicSuffixList merge(PublicSuffixList a, PublicSuffixList b) {
|
||||
List<String> ar = a.getRules();
|
||||
List<String> ae = a.getExceptions();
|
||||
List<String> br = b.getRules();
|
||||
List<String> be = b.getExceptions();
|
||||
List<String> cr = new ArrayList<String>(ar.size() + br.size());
|
||||
List<String> ce = new ArrayList<String>(ae.size() + be.size());
|
||||
cr.addAll(ar);
|
||||
cr.addAll(br);
|
||||
ce.addAll(ae);
|
||||
ce.addAll(be);
|
||||
return new PublicSuffixList(cr, ce);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read in the country file and add all TLDs to the list.
|
||||
* It would almost be easier just to add all possible 26*26 two-letter codes.
|
||||
*
|
||||
* @param tlds out parameter
|
||||
* @since 0.9.20 adapted from GeoIP.loadCountryFile()
|
||||
*/
|
||||
private static void addCountries(I2PAppContext ctx, List<String> tlds) {
|
||||
Log log = ctx.logManager().getLog(I2PSSLSocketFactory.class);
|
||||
String geoDir = ctx.getProperty(PROP_GEOIP_DIR, GEOIP_DIR_DEFAULT);
|
||||
File geoFile = new File(geoDir);
|
||||
if (!geoFile.isAbsolute())
|
||||
geoFile = new File(ctx.getBaseDir(), geoDir);
|
||||
geoFile = new File(geoFile, COUNTRY_FILE_DEFAULT);
|
||||
if (!geoFile.exists()) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("Country file not found: " + geoFile.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
br = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(geoFile), "UTF-8"));
|
||||
String line = null;
|
||||
int i = 0;
|
||||
while ( (line = br.readLine()) != null) {
|
||||
try {
|
||||
if (line.charAt(0) == '#')
|
||||
continue;
|
||||
String[] s = line.split(",");
|
||||
String lc = s[0].toLowerCase(Locale.US);
|
||||
tlds.add(lc);
|
||||
i++;
|
||||
} catch (IndexOutOfBoundsException ioobe) {}
|
||||
}
|
||||
if (log.shouldInfo())
|
||||
log.info("Loaded " + i + " TLDs from " + geoFile.getAbsolutePath());
|
||||
} catch (IOException ioe) {
|
||||
log.error("Error reading the Country File", ioe);
|
||||
} finally {
|
||||
if (br != null) try { br.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads certs from
|
||||
* the ~/.i2p/certificates/ and $I2P/certificates/ directories.
|
||||
|
||||
@@ -76,6 +76,29 @@ public class I2PThread extends Thread {
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* Overridden to provide useful info to users on OOM, and to prevent
|
||||
* shutting down the whole JVM for what is most likely not a heap issue.
|
||||
* If the calling thread is an I2PThread an OOM would shut down the JVM.
|
||||
* Telling the user to increase the heap size may make the problem worse.
|
||||
* We may be able to continue without this thread, particularly in app context.
|
||||
*
|
||||
* @since 0.9.20
|
||||
*/
|
||||
@Override
|
||||
public void start() {
|
||||
try {
|
||||
super.start();
|
||||
} catch (OutOfMemoryError oom) {
|
||||
System.out.println("ERROR: Thread could not be started: " + getName());
|
||||
if (!(SystemVersion.isWindows() || SystemVersion.isAndroid())) {
|
||||
System.out.println("Check ulimit -u, /etc/security/limits.conf, or /proc/sys/kernel/threads-max");
|
||||
}
|
||||
oom.printStackTrace();
|
||||
throw new RuntimeException("Thread could not be started", oom);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
//_name = Thread.currentThread().getName();
|
||||
|
||||
@@ -184,6 +184,26 @@ public class Log {
|
||||
public boolean shouldLog(int priority) {
|
||||
return priority >= _minPriority;
|
||||
}
|
||||
|
||||
/** @since 0.9.20 */
|
||||
public boolean shouldDebug() {
|
||||
return shouldLog(DEBUG);
|
||||
}
|
||||
|
||||
/** @since 0.9.20 */
|
||||
public boolean shouldInfo() {
|
||||
return shouldLog(INFO);
|
||||
}
|
||||
|
||||
/** @since 0.9.20 */
|
||||
public boolean shouldWarn() {
|
||||
return shouldLog(WARN);
|
||||
}
|
||||
|
||||
/** @since 0.9.20 */
|
||||
public boolean shouldError() {
|
||||
return shouldLog(ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* logs a loop when closing a resource with level INFO
|
||||
|
||||
@@ -74,6 +74,9 @@ class LogWriter extends LogWriterBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.19
|
||||
*/
|
||||
protected void flushWriter() {
|
||||
try {
|
||||
if (_currentOut != null)
|
||||
@@ -84,6 +87,9 @@ class LogWriter extends LogWriterBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.19 renamed from closeFile()
|
||||
*/
|
||||
protected void closeWriter() {
|
||||
Writer out = _currentOut;
|
||||
if (out != null) {
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.Queue;
|
||||
* the log. This also periodically instructs the LogManager to reread its config
|
||||
* file.
|
||||
*
|
||||
* @since 0.9.19 pulled from LogWriter so Android may extend
|
||||
*/
|
||||
abstract class LogWriterBase implements Runnable {
|
||||
/** every 10 seconds? why? Just have the gui force a reread after a change?? */
|
||||
|
||||
@@ -196,15 +196,14 @@ public class NativeBigInteger extends BigInteger {
|
||||
private final static String sCPUType; //The CPU Type to optimize for (one of the above strings)
|
||||
|
||||
static {
|
||||
if (_isX86) {// Don't try to resolve CPU type on non x86 hardware
|
||||
if (_isX86) { // Don't try to resolve CPU type on non x86 hardware
|
||||
sCPUType = resolveCPUType();
|
||||
}
|
||||
else if (_isArm) {
|
||||
} else if (_isArm) {
|
||||
sCPUType = JBIGI_OPTIMIZATION_ARM;
|
||||
} else if (_isPPC && !_isMac) {
|
||||
sCPUType = JBIGI_OPTIMIZATION_PPC;
|
||||
sCPUType = JBIGI_OPTIMIZATION_PPC;
|
||||
} else {
|
||||
sCPUType = null;
|
||||
sCPUType = null;
|
||||
}
|
||||
loadNative();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package net.i2p.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@@ -73,4 +78,18 @@ public class PortMapper {
|
||||
return def;
|
||||
return port.intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging only
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void renderStatusHTML(Writer out) throws IOException {
|
||||
List<String> services = new ArrayList(_dir.keySet());
|
||||
out.write("<h2>Port Mapper</h2><table><tr><th>Service<th>Port\n");
|
||||
Collections.sort(services);
|
||||
for (String s : services) {
|
||||
out.write("<tr><td>" + s + "<td>" + _dir.get(s) + '\n');
|
||||
}
|
||||
out.write("</table>\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +168,21 @@ public class ResettableGZIPInputStream extends InflaterInputStream {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does NOT call super.close(), as it cannot be reused if we do that.
|
||||
* Broken before 0.9.20.
|
||||
* @since 0.9.20
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
len = 0;
|
||||
inf.reset();
|
||||
_complete = false;
|
||||
_crc32.reset();
|
||||
_buf1[0] = 0x0;
|
||||
_extraByteInputStream.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moved from i2ptunnel HTTPResponseOutputStream.InternalGZIPInputStream
|
||||
* @since 0.8.9
|
||||
|
||||
@@ -44,6 +44,7 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.KeyStore;
|
||||
@@ -54,7 +55,7 @@ import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
@@ -82,7 +83,9 @@ import net.i2p.data.DataHelper;
|
||||
*/
|
||||
public class SSLEepGet extends EepGet {
|
||||
/** if true, save cert chain on cert error */
|
||||
private boolean _saveCerts;
|
||||
private int _saveCerts;
|
||||
/** if true, don't do hostname verification */
|
||||
private boolean _bypassVerification;
|
||||
/** true if called from main(), used for logging */
|
||||
private boolean _commandLine;
|
||||
/** may be null if init failed */
|
||||
@@ -153,15 +156,20 @@ public class SSLEepGet extends EepGet {
|
||||
* SSLEepGet -s https://foo/bar
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
boolean saveCerts = false;
|
||||
int saveCerts = 0;
|
||||
boolean noVerify = false;
|
||||
boolean error = false;
|
||||
Getopt g = new Getopt("ssleepget", args, "s");
|
||||
Getopt g = new Getopt("ssleepget", args, "sz");
|
||||
try {
|
||||
int c;
|
||||
while ((c = g.getopt()) != -1) {
|
||||
switch (c) {
|
||||
case 's':
|
||||
saveCerts = true;
|
||||
saveCerts++;
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
noVerify = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
@@ -193,8 +201,10 @@ public class SSLEepGet extends EepGet {
|
||||
}
|
||||
|
||||
SSLEepGet get = new SSLEepGet(I2PAppContext.getGlobalContext(), out, url);
|
||||
if (saveCerts)
|
||||
get._saveCerts = true;
|
||||
if (saveCerts > 0)
|
||||
get._saveCerts = saveCerts;
|
||||
if (noVerify)
|
||||
get._bypassVerification = true;
|
||||
get._commandLine = true;
|
||||
get.addStatusListener(get.new CLIStatusListener(1024, 40));
|
||||
if(!get.fetch(45*1000, -1, 60*1000))
|
||||
@@ -202,8 +212,10 @@ public class SSLEepGet extends EepGet {
|
||||
}
|
||||
|
||||
private static void usage() {
|
||||
System.err.println("Usage: SSLEepGet https://url\n" +
|
||||
"To save unknown certs, use: SSLEepGet -s https://url");
|
||||
System.err.println("Usage: SSLEepGet [-sz] https://url\n" +
|
||||
" -s save unknown certs\n" +
|
||||
" -s -s save all certs\n" +
|
||||
" -z bypass hostname verification");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,7 +363,7 @@ public class SSLEepGet extends EepGet {
|
||||
for (int k = 0; k < chain.length; k++) {
|
||||
X509Certificate cert = chain[k];
|
||||
String name = host + '-' + (k + 1) + ".crt";
|
||||
System.out.println("NOTE: Saving untrusted X509 certificate as " + name);
|
||||
System.out.println("NOTE: Saving X509 certificate as " + name);
|
||||
System.out.println(" Issuer: " + cert.getIssuerX500Principal());
|
||||
System.out.println(" Valid From: " + cert.getNotBefore());
|
||||
System.out.println(" Valid To: " + cert.getNotAfter());
|
||||
@@ -363,7 +375,6 @@ public class SSLEepGet extends EepGet {
|
||||
CertUtil.saveCert(cert, new File(name));
|
||||
}
|
||||
System.out.println("NOTE: To trust them, copy the certificate file(s) to the certificates directory and rerun without the -s option");
|
||||
System.out.println("NOTE: EepGet failed, certificate error follows:");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -409,6 +420,10 @@ public class SSLEepGet extends EepGet {
|
||||
else
|
||||
timeout.setInactivityTimeout(60*1000);
|
||||
}
|
||||
if (_fetchInactivityTimeout > 0)
|
||||
_proxy.setSoTimeout(_fetchInactivityTimeout);
|
||||
else
|
||||
_proxy.setSoTimeout(INACTIVITY_TIMEOUT);
|
||||
|
||||
if (_redirectLocation != null) {
|
||||
throw new IOException("Server redirect to " + _redirectLocation + " not allowed");
|
||||
@@ -549,12 +564,28 @@ public class SSLEepGet extends EepGet {
|
||||
port = url.getPort();
|
||||
if (port == -1)
|
||||
port = 443;
|
||||
// Warning, createSocket() followed by connect(InetSocketAddress)
|
||||
// disables SNI, at least on Java 7.
|
||||
// So we must do createSocket(host, port) and then setSoTimeout;
|
||||
// we can't crate a disconnected socket and then call setSoTimeout, sadly.
|
||||
if (_sslContext != null)
|
||||
_proxy = _sslContext.getSocketFactory().createSocket(host, port);
|
||||
else
|
||||
_proxy = SSLSocketFactory.getDefault().createSocket(host, port);
|
||||
if (_fetchHeaderTimeout > 0) {
|
||||
_proxy.setSoTimeout(_fetchHeaderTimeout);
|
||||
}
|
||||
SSLSocket socket = (SSLSocket) _proxy;
|
||||
I2PSSLSocketFactory.setProtocolsAndCiphers(socket);
|
||||
if (!_bypassVerification) {
|
||||
try {
|
||||
I2PSSLSocketFactory.verifyHostname(_context, socket, host);
|
||||
} catch (SSLException ssle) {
|
||||
if (_saveCerts > 0 && _stm != null)
|
||||
saveCerts(host, _stm);
|
||||
throw ssle;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new MalformedURLException("Only https supported: " + _actualURL);
|
||||
}
|
||||
@@ -570,12 +601,14 @@ public class SSLEepGet extends EepGet {
|
||||
try {
|
||||
_proxyOut.write(DataHelper.getUTF8(req));
|
||||
_proxyOut.flush();
|
||||
} catch (SSLHandshakeException sslhe) {
|
||||
if (_saveCerts > 1 && _stm != null)
|
||||
saveCerts(host, _stm);
|
||||
} catch (SSLException sslhe) {
|
||||
// this maybe would be better done in the catch in super.fetch(), but
|
||||
// then we'd have to copy it all over here.
|
||||
_log.error("SSL negotiation error with " + host + ':' + port +
|
||||
" - self-signed certificate or untrusted certificate authority?", sslhe);
|
||||
if (_saveCerts && _stm != null)
|
||||
if (_saveCerts > 0 && _stm != null)
|
||||
saveCerts(host, _stm);
|
||||
else if (_commandLine) {
|
||||
System.out.println("FAILED (probably due to untrusted certificates) - Run with -s option to save certificates");
|
||||
|
||||
@@ -24,12 +24,15 @@ import net.i2p.I2PAppContext;
|
||||
* For periodic events, use addPeriodicEvent(). Unlike SimpleTimer,
|
||||
* uncaught Exceptions will not prevent subsequent executions.
|
||||
*
|
||||
* @deprecated in 0.9.20, use SimpleTimer2 instead
|
||||
*
|
||||
* @author zzz
|
||||
*/
|
||||
public class SimpleScheduler {
|
||||
|
||||
/**
|
||||
* If you have a context, use context.simpleScheduler() instead
|
||||
* @deprecated in 0.9.20, replaced by SimpleTimer2
|
||||
*/
|
||||
public static SimpleScheduler getInstance() {
|
||||
return I2PAppContext.getGlobalContext().simpleScheduler();
|
||||
@@ -46,6 +49,7 @@ public class SimpleScheduler {
|
||||
/**
|
||||
* To be instantiated by the context.
|
||||
* Others should use context.simpleTimer() instead
|
||||
* @deprecated in 0.9.20, replaced by SimpleTimer2
|
||||
*/
|
||||
public SimpleScheduler(I2PAppContext context) {
|
||||
this(context, "SimpleScheduler");
|
||||
@@ -54,6 +58,7 @@ public class SimpleScheduler {
|
||||
/**
|
||||
* To be instantiated by the context.
|
||||
* Others should use context.simpleTimer() instead
|
||||
* @deprecated in 0.9.20, replaced by SimpleTimer2
|
||||
*/
|
||||
private SimpleScheduler(I2PAppContext context, String name) {
|
||||
_log = context.logManager().getLog(SimpleScheduler.class);
|
||||
|
||||
@@ -14,7 +14,7 @@ import net.i2p.I2PAppContext;
|
||||
* they b0rk the timer).
|
||||
*
|
||||
* WARNING - Deprecated.
|
||||
* This is an inefficient mess. Use SimpleScheduler or SimpleTimer2 if possible.
|
||||
* This is an inefficient mess. Use SimpleTimer2 if possible.
|
||||
*/
|
||||
public class SimpleTimer {
|
||||
|
||||
|
||||
@@ -124,6 +124,81 @@ public class SimpleTimer2 {
|
||||
private ScheduledFuture schedule(TimedEvent t, long timeoutMs) {
|
||||
return _executor.schedule(t, timeoutMs, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue up the given event to be fired no sooner than timeoutMs from now.
|
||||
*
|
||||
* For transition from SimpleScheduler. Uncancellable.
|
||||
* New code should use SimpleTimer2.TimedEvent.
|
||||
*
|
||||
* @param event to be run once
|
||||
* @param timeoutMs run after this delay
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void addEvent(final SimpleTimer.TimedEvent event, final long timeoutMs) {
|
||||
if (event == null)
|
||||
throw new IllegalArgumentException("addEvent null");
|
||||
|
||||
new TimedEvent(this, timeoutMs) {
|
||||
@Override
|
||||
public void timeReached() {
|
||||
event.timeReached();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return event.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule periodic event
|
||||
*
|
||||
* The TimedEvent must not do its own rescheduling.
|
||||
* As all Exceptions are caught in run(), these will not prevent
|
||||
* subsequent executions (unlike SimpleTimer, where the TimedEvent does
|
||||
* its own rescheduling).
|
||||
*
|
||||
* For transition from SimpleScheduler. Uncancellable.
|
||||
* New code should use SimpleTimer2.TimedEvent.
|
||||
*
|
||||
* @since 0.9.20
|
||||
* @param timeoutMs run first and subsequent iterations of this event every timeoutMs ms
|
||||
*/
|
||||
public void addPeriodicEvent(final SimpleTimer.TimedEvent event, final long timeoutMs) {
|
||||
addPeriodicEvent(event, timeoutMs, timeoutMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule periodic event
|
||||
*
|
||||
* The TimedEvent must not do its own rescheduling.
|
||||
* As all Exceptions are caught in run(), these will not prevent
|
||||
* subsequent executions (unlike SimpleTimer, where the TimedEvent does
|
||||
* its own rescheduling).
|
||||
*
|
||||
* For transition from SimpleScheduler. Uncancellable.
|
||||
* New code should use SimpleTimer2.TimedEvent.
|
||||
*
|
||||
* @since 0.9.20
|
||||
* @param delay run the first iteration of this event after delay ms
|
||||
* @param timeoutMs run subsequent iterations of this event every timeoutMs ms
|
||||
*/
|
||||
public void addPeriodicEvent(final SimpleTimer.TimedEvent event, final long delay, final long timeoutMs) {
|
||||
|
||||
new PeriodicTimedEvent(this, delay, timeoutMs) {
|
||||
@Override
|
||||
public void timeReached() {
|
||||
event.timeReached();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return event.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* state of a given TimedEvent
|
||||
@@ -141,6 +216,7 @@ public class SimpleTimer2 {
|
||||
CANCELLED
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Similar to SimpleTimer.TimedEvent but users must extend instead of implement,
|
||||
* and all schedule and cancel methods are through this class rather than SimpleTimer2.
|
||||
@@ -228,7 +304,6 @@ public class SimpleTimer2 {
|
||||
break;
|
||||
case SCHEDULED: // nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -403,5 +478,30 @@ public class SimpleTimer2 {
|
||||
" Completed: " + _executor.getCompletedTaskCount() +
|
||||
" Queued: " + _executor.getQueue().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* For transition from SimpleScheduler.
|
||||
* @since 0.9.20
|
||||
*/
|
||||
private static abstract class PeriodicTimedEvent extends TimedEvent {
|
||||
private long _timeoutMs;
|
||||
|
||||
/**
|
||||
* Schedule periodic event
|
||||
*
|
||||
* @param delay run the first iteration of this event after delay ms
|
||||
* @param timeoutMs run subsequent iterations of this event every timeoutMs ms
|
||||
*/
|
||||
public PeriodicTimedEvent(SimpleTimer2 pool, long delay, long timeoutMs) {
|
||||
super(pool, delay);
|
||||
_timeoutMs = timeoutMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
schedule(_timeoutMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,9 @@ package net.metanotion.io.block.index;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@@ -61,7 +63,16 @@ public class BSkipLevels extends SkipLevels {
|
||||
public final BlockFile bf;
|
||||
private final BSkipList bsl;
|
||||
private boolean isKilled;
|
||||
// the level pages, passed from the constructor to initializeLevels(),
|
||||
// NOT kept up to date
|
||||
private final int[] lps;
|
||||
|
||||
/**
|
||||
* Non-recursive initializer initializeLevels()
|
||||
* MUST be called on the first BSkipLevel in the skiplist
|
||||
* after the constructor, unless it's a new empty
|
||||
* level and init() was previously called.
|
||||
*/
|
||||
public BSkipLevels(BlockFile bf, int levelPage, BSkipList bsl) throws IOException {
|
||||
this.levelPage = levelPage;
|
||||
this.bf = bf;
|
||||
@@ -88,25 +99,57 @@ public class BSkipLevels extends SkipLevels {
|
||||
|
||||
this.levels = new BSkipLevels[maxLen];
|
||||
if (bf.log.shouldLog(Log.DEBUG))
|
||||
bf.log.debug("Reading New BSkipLevels with " + nonNull + " / " + maxLen + " valid levels page " + levelPage);
|
||||
bf.log.debug("Reading New BSkipLevels with " + nonNull + " / " + maxLen + " valid levels page " + levelPage +
|
||||
" in skiplist " + bsl);
|
||||
// We have to read now because new BSkipLevels() will move the file pointer
|
||||
int[] lps = new int[nonNull];
|
||||
lps = new int[nonNull];
|
||||
for(int i = 0; i < nonNull; i++) {
|
||||
lps[i] = bf.file.readUnsignedInt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-recursive initializer.
|
||||
* MUST be called on the first BSkipLevel in the skiplist
|
||||
* after the constructor, unless it's a new empty
|
||||
* level and init() was previously called.
|
||||
* Only call on the first skiplevel in the list!
|
||||
*
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void initializeLevels() {
|
||||
List<BSkipLevels> toInit = new ArrayList<BSkipLevels>(32);
|
||||
List<BSkipLevels> nextInit = new ArrayList<BSkipLevels>(32);
|
||||
initializeLevels(toInit);
|
||||
while (!toInit.isEmpty()) {
|
||||
for (BSkipLevels bsl : toInit) {
|
||||
bsl.initializeLevels(nextInit);
|
||||
}
|
||||
List<BSkipLevels> tmp = toInit;
|
||||
toInit = nextInit;
|
||||
nextInit = tmp;
|
||||
nextInit.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-recursive initializer.
|
||||
* MUST be called after constructor.
|
||||
*
|
||||
* @param nextInit out parameter, next levels to initialize
|
||||
* @since 0.9.20
|
||||
*/
|
||||
private void initializeLevels(List<BSkipLevels> nextInit) {
|
||||
boolean fail = false;
|
||||
for(int i = 0; i < nonNull; i++) {
|
||||
for(int i = 0; i < lps.length; i++) {
|
||||
int lp = lps[i];
|
||||
if(lp != 0) {
|
||||
levels[i] = bsl.levelHash.get(Integer.valueOf(lp));
|
||||
if(levels[i] == null) {
|
||||
try {
|
||||
// FIXME this will explode the stack if too big
|
||||
// Redo this without recursion?
|
||||
// Lots of recursion in super to be fixed also...
|
||||
levels[i] = new BSkipLevels(bf, lp, bsl);
|
||||
bsl.levelHash.put(Integer.valueOf(lp), levels[i]);
|
||||
BSkipLevels lev = new BSkipLevels(bf, lp, bsl);
|
||||
levels[i] = lev;
|
||||
nextInit.add(lev);
|
||||
} catch (IOException ioe) {
|
||||
bf.log.error("Corrupt database, bad level " + i +
|
||||
" at page " + lp, ioe);
|
||||
@@ -129,7 +172,9 @@ public class BSkipLevels extends SkipLevels {
|
||||
// TODO also check that the level[] array is not out-of-order
|
||||
} else {
|
||||
if (bf.log.shouldLog(Log.WARN))
|
||||
bf.log.warn("WTF " + this + " i = " + i + " of " + nonNull + " / " + maxLen + " valid levels but page is zero");
|
||||
bf.log.warn("WTF " + this + " i = " + i + " of " +
|
||||
lps.length + " / " + levels.length +
|
||||
" valid levels but page is zero");
|
||||
levels[i] = null;
|
||||
fail = true;
|
||||
}
|
||||
@@ -200,6 +245,7 @@ public class BSkipLevels extends SkipLevels {
|
||||
if (bf.log.shouldLog(Log.DEBUG))
|
||||
bf.log.debug("New BSkipLevels height " + levels + " page " + page);
|
||||
return new BSkipLevels(bf, page, bsl);
|
||||
// do not need to call initLevels() here
|
||||
} catch (IOException ioe) { throw new RuntimeException("Error creating database page", ioe); }
|
||||
}
|
||||
|
||||
@@ -382,7 +428,8 @@ public class BSkipLevels extends SkipLevels {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String rv = "BSL height: " + levels.length + " page: " + levelPage + " span: " + bottom;
|
||||
String rv = "BSLevel height: " + levels.length + " page: " + levelPage + " span: " + bottom +
|
||||
" in skiplist " + bsl;
|
||||
if (isKilled)
|
||||
rv += " KILLED";
|
||||
return rv;
|
||||
|
||||
@@ -91,7 +91,9 @@ public class BSkipList extends SkipList {
|
||||
first = new IBSkipSpan(bf, this, firstSpanPage, key, val);
|
||||
else
|
||||
first = new BSkipSpan(bf, this, firstSpanPage, key, val);
|
||||
stack = new BSkipLevels(bf, firstLevelPage, this);
|
||||
BSkipLevels bstack = new BSkipLevels(bf, firstLevelPage, this);
|
||||
bstack.initializeLevels();
|
||||
stack = bstack;
|
||||
int total = 0;
|
||||
for (BSkipSpan ss : spanHash.values()) {
|
||||
total += ss.nKeys;
|
||||
|
||||
Reference in New Issue
Block a user