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:
@ -60,7 +60,7 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
{
|
||||
// http://en.wikipedia.org/wiki/Cpuid
|
||||
// http://web.archive.org/web/20110307080258/http://www.intel.com/Assets/PDF/appnote/241618.pdf
|
||||
// http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-2a-manual.pdf
|
||||
// http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf
|
||||
String modelString = null;
|
||||
int family = CPUID.getCPUFamily();
|
||||
int model = CPUID.getCPUModel();
|
||||
@ -156,9 +156,8 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
isPentiumMCompatible = true;
|
||||
isCore2Compatible = true;
|
||||
isX64 = true;
|
||||
}
|
||||
if (extmodel >= 2) {
|
||||
isCoreiCompatible = true;
|
||||
if (extmodel >= 2)
|
||||
isCoreiCompatible = true;
|
||||
}
|
||||
switch (model) {
|
||||
case 0:
|
||||
@ -350,6 +349,11 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
// most flags are set above
|
||||
// isCoreiCompatible = true is the default
|
||||
|
||||
// 22 nm
|
||||
case 0x45:
|
||||
modelString = "Mobile Celeron";
|
||||
break;
|
||||
|
||||
// Atom Silvermont / Bay Trail / Avoton 22 nm
|
||||
// Supports SSE 4.2
|
||||
case 0x4d:
|
||||
|
@ -1,7 +1,9 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
CPUID implementaion, borrowed from freenet, updated and heavily modified
|
||||
CPUID implementaion, borrowed from freenet, updated and heavily modified.
|
||||
|
||||
Not recommended for external use, not maintained as a public API.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -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;
|
||||
|
@ -0,0 +1,296 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.http.conn.ssl;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import javax.naming.InvalidNameException;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.ldap.LdapName;
|
||||
import javax.naming.ldap.Rdn;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
import org.apache.http.conn.util.PublicSuffixMatcher;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Default {@link javax.net.ssl.HostnameVerifier} implementation.
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
public final class DefaultHostnameVerifier implements HostnameVerifier {
|
||||
|
||||
final static int DNS_NAME_TYPE = 2;
|
||||
final static int IP_ADDRESS_TYPE = 7;
|
||||
|
||||
private final Log log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
|
||||
|
||||
private final PublicSuffixMatcher publicSuffixMatcher;
|
||||
|
||||
public DefaultHostnameVerifier(final PublicSuffixMatcher publicSuffixMatcher) {
|
||||
this.publicSuffixMatcher = publicSuffixMatcher;
|
||||
}
|
||||
|
||||
public DefaultHostnameVerifier() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean verify(final String host, final SSLSession session) {
|
||||
try {
|
||||
final Certificate[] certs = session.getPeerCertificates();
|
||||
final X509Certificate x509 = (X509Certificate) certs[0];
|
||||
verify(host, x509);
|
||||
return true;
|
||||
} catch(final SSLException ex) {
|
||||
if (log.shouldWarn()) {
|
||||
log.warn(ex.getMessage(), ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public final void verify(
|
||||
final String host, final X509Certificate cert) throws SSLException {
|
||||
final boolean ipv4 = InetAddressUtils.isIPv4Address(host);
|
||||
final boolean ipv6 = InetAddressUtils.isIPv6Address(host);
|
||||
final int subjectType = ipv4 || ipv6 ? IP_ADDRESS_TYPE : DNS_NAME_TYPE;
|
||||
final List<String> subjectAlts = extractSubjectAlts(cert, subjectType);
|
||||
if (subjectAlts != null && !subjectAlts.isEmpty()) {
|
||||
if (ipv4) {
|
||||
matchIPAddress(host, subjectAlts);
|
||||
} else if (ipv6) {
|
||||
matchIPv6Address(host, subjectAlts);
|
||||
} else {
|
||||
matchDNSName(host, subjectAlts, this.publicSuffixMatcher);
|
||||
}
|
||||
} else {
|
||||
// CN matching has been deprecated by rfc2818 and can be used
|
||||
// as fallback only when no subjectAlts are available
|
||||
final X500Principal subjectPrincipal = cert.getSubjectX500Principal();
|
||||
final String cn = extractCN(subjectPrincipal.getName(X500Principal.RFC2253));
|
||||
if (cn == null) {
|
||||
throw new SSLException("Certificate subject for <" + host + "> doesn't contain " +
|
||||
"a common name and does not have alternative names");
|
||||
}
|
||||
matchCN(host, cn, this.publicSuffixMatcher);
|
||||
}
|
||||
}
|
||||
|
||||
static void matchIPAddress(final String host, final List<String> subjectAlts) throws SSLException {
|
||||
for (int i = 0; i < subjectAlts.size(); i++) {
|
||||
final String subjectAlt = subjectAlts.get(i);
|
||||
if (host.equals(subjectAlt)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new SSLException("Certificate for <" + host + "> doesn't match any " +
|
||||
"of the subject alternative names: " + subjectAlts);
|
||||
}
|
||||
|
||||
static void matchIPv6Address(final String host, final List<String> subjectAlts) throws SSLException {
|
||||
final String normalisedHost = normaliseAddress(host);
|
||||
for (int i = 0; i < subjectAlts.size(); i++) {
|
||||
final String subjectAlt = subjectAlts.get(i);
|
||||
final String normalizedSubjectAlt = normaliseAddress(subjectAlt);
|
||||
if (normalisedHost.equals(normalizedSubjectAlt)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new SSLException("Certificate for <" + host + "> doesn't match any " +
|
||||
"of the subject alternative names: " + subjectAlts);
|
||||
}
|
||||
|
||||
static void matchDNSName(final String host, final List<String> subjectAlts,
|
||||
final PublicSuffixMatcher publicSuffixMatcher) throws SSLException {
|
||||
final String normalizedHost = host.toLowerCase(Locale.ROOT);
|
||||
for (int i = 0; i < subjectAlts.size(); i++) {
|
||||
final String subjectAlt = subjectAlts.get(i);
|
||||
final String normalizedSubjectAlt = subjectAlt.toLowerCase(Locale.ROOT);
|
||||
if (matchIdentityStrict(normalizedHost, normalizedSubjectAlt, publicSuffixMatcher)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new SSLException("Certificate for <" + host + "> doesn't match any " +
|
||||
"of the subject alternative names: " + subjectAlts);
|
||||
}
|
||||
|
||||
static void matchCN(final String host, final String cn,
|
||||
final PublicSuffixMatcher publicSuffixMatcher) throws SSLException {
|
||||
if (!matchIdentityStrict(host, cn, publicSuffixMatcher)) {
|
||||
throw new SSLException("Certificate for <" + host + "> doesn't match " +
|
||||
"common name of the certificate subject: " + cn);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean matchDomainRoot(final String host, final String domainRoot) {
|
||||
if (domainRoot == null) {
|
||||
return false;
|
||||
}
|
||||
return host.endsWith(domainRoot) && (host.length() == domainRoot.length()
|
||||
|| host.charAt(host.length() - domainRoot.length() - 1) == '.');
|
||||
}
|
||||
|
||||
private static boolean matchIdentity(final String host, final String identity,
|
||||
final PublicSuffixMatcher publicSuffixMatcher,
|
||||
final boolean strict) {
|
||||
if (publicSuffixMatcher != null && host.contains(".")) {
|
||||
if (!matchDomainRoot(host, publicSuffixMatcher.getDomainRoot(identity))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// RFC 2818, 3.1. Server Identity
|
||||
// "...Names may contain the wildcard
|
||||
// character * which is considered to match any single domain name
|
||||
// component or component fragment..."
|
||||
// Based on this statement presuming only singular wildcard is legal
|
||||
final int asteriskIdx = identity.indexOf('*');
|
||||
if (asteriskIdx != -1) {
|
||||
final String prefix = identity.substring(0, asteriskIdx);
|
||||
final String suffix = identity.substring(asteriskIdx + 1);
|
||||
if (!prefix.isEmpty() && !host.startsWith(prefix)) {
|
||||
return false;
|
||||
}
|
||||
if (!suffix.isEmpty() && !host.endsWith(suffix)) {
|
||||
return false;
|
||||
}
|
||||
// Additional sanity checks on content selected by wildcard can be done here
|
||||
if (strict) {
|
||||
final String remainder = host.substring(
|
||||
prefix.length(), host.length() - suffix.length());
|
||||
if (remainder.contains(".")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return host.equalsIgnoreCase(identity);
|
||||
}
|
||||
|
||||
static boolean matchIdentity(final String host, final String identity,
|
||||
final PublicSuffixMatcher publicSuffixMatcher) {
|
||||
return matchIdentity(host, identity, publicSuffixMatcher, false);
|
||||
}
|
||||
|
||||
static boolean matchIdentity(final String host, final String identity) {
|
||||
return matchIdentity(host, identity, null, false);
|
||||
}
|
||||
|
||||
static boolean matchIdentityStrict(final String host, final String identity,
|
||||
final PublicSuffixMatcher publicSuffixMatcher) {
|
||||
return matchIdentity(host, identity, publicSuffixMatcher, true);
|
||||
}
|
||||
|
||||
static boolean matchIdentityStrict(final String host, final String identity) {
|
||||
return matchIdentity(host, identity, null, true);
|
||||
}
|
||||
|
||||
static String extractCN(final String subjectPrincipal) throws SSLException {
|
||||
if (subjectPrincipal == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final LdapName subjectDN = new LdapName(subjectPrincipal);
|
||||
final List<Rdn> rdns = subjectDN.getRdns();
|
||||
for (int i = rdns.size() - 1; i >= 0; i--) {
|
||||
final Rdn rds = rdns.get(i);
|
||||
final Attributes attributes = rds.toAttributes();
|
||||
final Attribute cn = attributes.get("cn");
|
||||
if (cn != null) {
|
||||
try {
|
||||
final Object value = cn.get();
|
||||
if (value != null) {
|
||||
return value.toString();
|
||||
}
|
||||
} catch (NoSuchElementException ignore) {
|
||||
} catch (NamingException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (InvalidNameException e) {
|
||||
throw new SSLException(subjectPrincipal + " is not a valid X500 distinguished name");
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> extractSubjectAlts(final X509Certificate cert, final int subjectType) {
|
||||
Collection<List<?>> c = null;
|
||||
try {
|
||||
c = cert.getSubjectAlternativeNames();
|
||||
} catch(final CertificateParsingException ignore) {
|
||||
}
|
||||
List<String> subjectAltList = null;
|
||||
if (c != null) {
|
||||
for (final List<?> aC : c) {
|
||||
final List<?> list = aC;
|
||||
final int type = ((Integer) list.get(0)).intValue();
|
||||
if (type == subjectType) {
|
||||
final String s = (String) list.get(1);
|
||||
if (subjectAltList == null) {
|
||||
subjectAltList = new ArrayList<String>();
|
||||
}
|
||||
subjectAltList.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
return subjectAltList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalize IPv6 or DNS name.
|
||||
*/
|
||||
static String normaliseAddress(final String hostname) {
|
||||
if (hostname == null) {
|
||||
return hostname;
|
||||
}
|
||||
try {
|
||||
final InetAddress inetAddress = InetAddress.getByName(hostname);
|
||||
return inetAddress.getHostAddress();
|
||||
} catch (final UnknownHostException unexpected) { // Should not happen, because we check for IPv6 address above
|
||||
return hostname;
|
||||
}
|
||||
}
|
||||
}
|
8
core/java/src/org/apache/http/conn/ssl/package.html
Normal file
8
core/java/src/org/apache/http/conn/ssl/package.html
Normal file
@ -0,0 +1,8 @@
|
||||
<html><body>
|
||||
<p>
|
||||
From Apache HttpClient 4.4.1.
|
||||
For I2PSSSLSocketFactory.
|
||||
Small modifications to remove additional Apache dependencies.
|
||||
Apache 2.0 license.
|
||||
</p>
|
||||
</body></html>
|
122
core/java/src/org/apache/http/conn/util/InetAddressUtils.java
Normal file
122
core/java/src/org/apache/http/conn/util/InetAddressUtils.java
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.http.conn.util;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* A collection of utilities relating to InetAddresses.
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class InetAddressUtils {
|
||||
|
||||
private InetAddressUtils() {
|
||||
}
|
||||
|
||||
private static final String IPV4_BASIC_PATTERN_STRING =
|
||||
"(([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){1}" + // initial first field, 1-255
|
||||
"(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){2}" + // following 2 fields, 0-255 followed by .
|
||||
"([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"; // final field, 0-255
|
||||
|
||||
private static final Pattern IPV4_PATTERN =
|
||||
Pattern.compile("^" + IPV4_BASIC_PATTERN_STRING + "$");
|
||||
|
||||
private static final Pattern IPV4_MAPPED_IPV6_PATTERN = // TODO does not allow for redundant leading zeros
|
||||
Pattern.compile("^::[fF]{4}:" + IPV4_BASIC_PATTERN_STRING + "$");
|
||||
|
||||
private static final Pattern IPV6_STD_PATTERN =
|
||||
Pattern.compile(
|
||||
"^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$");
|
||||
|
||||
private static final Pattern IPV6_HEX_COMPRESSED_PATTERN =
|
||||
Pattern.compile(
|
||||
"^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)" + // 0-6 hex fields
|
||||
"::" +
|
||||
"(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)$"); // 0-6 hex fields
|
||||
|
||||
/*
|
||||
* The above pattern is not totally rigorous as it allows for more than 7 hex fields in total
|
||||
*/
|
||||
private static final char COLON_CHAR = ':';
|
||||
|
||||
// Must not have more than 7 colons (i.e. 8 fields)
|
||||
private static final int MAX_COLON_COUNT = 7;
|
||||
|
||||
/**
|
||||
* Checks whether the parameter is a valid IPv4 address
|
||||
*
|
||||
* @param input the address string to check for validity
|
||||
* @return true if the input parameter is a valid IPv4 address
|
||||
*/
|
||||
public static boolean isIPv4Address(final String input) {
|
||||
return IPV4_PATTERN.matcher(input).matches();
|
||||
}
|
||||
|
||||
public static boolean isIPv4MappedIPv64Address(final String input) {
|
||||
return IPV4_MAPPED_IPV6_PATTERN.matcher(input).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the parameter is a valid standard (non-compressed) IPv6 address
|
||||
*
|
||||
* @param input the address string to check for validity
|
||||
* @return true if the input parameter is a valid standard (non-compressed) IPv6 address
|
||||
*/
|
||||
public static boolean isIPv6StdAddress(final String input) {
|
||||
return IPV6_STD_PATTERN.matcher(input).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the parameter is a valid compressed IPv6 address
|
||||
*
|
||||
* @param input the address string to check for validity
|
||||
* @return true if the input parameter is a valid compressed IPv6 address
|
||||
*/
|
||||
public static boolean isIPv6HexCompressedAddress(final String input) {
|
||||
int colonCount = 0;
|
||||
for(int i = 0; i < input.length(); i++) {
|
||||
if (input.charAt(i) == COLON_CHAR) {
|
||||
colonCount++;
|
||||
}
|
||||
}
|
||||
return colonCount <= MAX_COLON_COUNT && IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the parameter is a valid IPv6 address (including compressed).
|
||||
*
|
||||
* @param input the address string to check for validity
|
||||
* @return true if the input parameter is a valid standard or compressed IPv6 address
|
||||
*/
|
||||
public static boolean isIPv6Address(final String input) {
|
||||
return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
package org.apache.http.conn.util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.http.util.Args;
|
||||
|
||||
/**
|
||||
* Public suffix is a set of DNS names or wildcards concatenated with dots. It represents
|
||||
* the part of a domain name which is not under the control of the individual registrant
|
||||
* <p>
|
||||
* An up-to-date list of suffixes can be obtained from
|
||||
* <a href="http://publicsuffix.org/">publicsuffix.org</a>
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
public final class PublicSuffixList {
|
||||
|
||||
private final List<String> rules;
|
||||
private final List<String> exceptions;
|
||||
|
||||
public PublicSuffixList(final List<String> rules, final List<String> exceptions) {
|
||||
this.rules = Collections.unmodifiableList(Args.notNull(rules, "Domain suffix rules"));
|
||||
this.exceptions = Collections.unmodifiableList(Args.notNull(exceptions, "Domain suffix exceptions"));
|
||||
}
|
||||
|
||||
public List<String> getRules() {
|
||||
return rules;
|
||||
}
|
||||
|
||||
public List<String> getExceptions() {
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
package org.apache.http.conn.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Parses the list from <a href="http://publicsuffix.org/">publicsuffix.org</a>
|
||||
* and configures a PublicSuffixFilter.
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
public final class PublicSuffixListParser {
|
||||
|
||||
private static final int MAX_LINE_LEN = 256;
|
||||
|
||||
public PublicSuffixListParser() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the public suffix list format. When creating the reader from the file, make sure to
|
||||
* use the correct encoding (the original list is in UTF-8).
|
||||
*
|
||||
* @param reader the data reader. The caller is responsible for closing the reader.
|
||||
* @throws java.io.IOException on error while reading from list
|
||||
*/
|
||||
public PublicSuffixList parse(final Reader reader) throws IOException {
|
||||
final List<String> rules = new ArrayList<String>();
|
||||
final List<String> exceptions = new ArrayList<String>();
|
||||
final BufferedReader r = new BufferedReader(reader);
|
||||
final StringBuilder sb = new StringBuilder(256);
|
||||
boolean more = true;
|
||||
while (more) {
|
||||
more = readLine(r, sb);
|
||||
String line = sb.toString();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith("//")) {
|
||||
continue; //entire lines can also be commented using //
|
||||
}
|
||||
if (line.startsWith(".")) {
|
||||
line = line.substring(1); // A leading dot is optional
|
||||
}
|
||||
// An exclamation mark (!) at the start of a rule marks an exception to a previous wildcard rule
|
||||
final boolean isException = line.startsWith("!");
|
||||
if (isException) {
|
||||
line = line.substring(1);
|
||||
}
|
||||
|
||||
if (isException) {
|
||||
exceptions.add(line);
|
||||
} else {
|
||||
rules.add(line);
|
||||
}
|
||||
}
|
||||
return new PublicSuffixList(rules, exceptions);
|
||||
}
|
||||
|
||||
private boolean readLine(final Reader r, final StringBuilder sb) throws IOException {
|
||||
sb.setLength(0);
|
||||
int b;
|
||||
boolean hitWhitespace = false;
|
||||
while ((b = r.read()) != -1) {
|
||||
final char c = (char) b;
|
||||
if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
// Each line is only read up to the first whitespace
|
||||
if (Character.isWhitespace(c)) {
|
||||
hitWhitespace = true;
|
||||
}
|
||||
if (!hitWhitespace) {
|
||||
sb.append(c);
|
||||
}
|
||||
if (sb.length() > MAX_LINE_LEN) {
|
||||
return false; // prevent excess memory usage
|
||||
}
|
||||
}
|
||||
return (b != -1);
|
||||
}
|
||||
|
||||
}
|
119
core/java/src/org/apache/http/conn/util/PublicSuffixMatcher.java
Normal file
119
core/java/src/org/apache/http/conn/util/PublicSuffixMatcher.java
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
package org.apache.http.conn.util;
|
||||
|
||||
import java.net.IDN;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.http.util.Args;
|
||||
|
||||
/**
|
||||
* Utility class that can test if DNS names match the content of the Public Suffix List.
|
||||
* <p>
|
||||
* An up-to-date list of suffixes can be obtained from
|
||||
* <a href="http://publicsuffix.org/">publicsuffix.org</a>
|
||||
*
|
||||
* @see org.apache.http.conn.util.PublicSuffixList
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
public final class PublicSuffixMatcher {
|
||||
|
||||
private final Map<String, String> rules;
|
||||
private final Map<String, String> exceptions;
|
||||
|
||||
public PublicSuffixMatcher(final Collection<String> rules, final Collection<String> exceptions) {
|
||||
Args.notNull(rules, "Domain suffix rules");
|
||||
this.rules = new ConcurrentHashMap<String, String>(rules.size());
|
||||
for (String rule: rules) {
|
||||
this.rules.put(rule, rule);
|
||||
}
|
||||
if (exceptions != null) {
|
||||
this.exceptions = new ConcurrentHashMap<String, String>(exceptions.size());
|
||||
for (String exception: exceptions) {
|
||||
this.exceptions.put(exception, exception);
|
||||
}
|
||||
} else {
|
||||
this.exceptions = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns registrable part of the domain for the given domain name of {@code null}
|
||||
* if given domain represents a public suffix.
|
||||
*
|
||||
* @param domain
|
||||
* @return domain root
|
||||
*/
|
||||
public String getDomainRoot(final String domain) {
|
||||
if (domain == null) {
|
||||
return null;
|
||||
}
|
||||
if (domain.startsWith(".")) {
|
||||
return null;
|
||||
}
|
||||
String domainName = null;
|
||||
String segment = domain.toLowerCase(Locale.ROOT);
|
||||
while (segment != null) {
|
||||
|
||||
// An exception rule takes priority over any other matching rule.
|
||||
if (this.exceptions != null && this.exceptions.containsKey(IDN.toUnicode(segment))) {
|
||||
return segment;
|
||||
}
|
||||
|
||||
if (this.rules.containsKey(IDN.toUnicode(segment))) {
|
||||
break;
|
||||
}
|
||||
|
||||
final int nextdot = segment.indexOf('.');
|
||||
final String nextSegment = nextdot != -1 ? segment.substring(nextdot + 1) : null;
|
||||
|
||||
if (nextSegment != null) {
|
||||
if (this.rules.containsKey("*." + IDN.toUnicode(nextSegment))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nextdot != -1) {
|
||||
domainName = segment;
|
||||
}
|
||||
segment = nextSegment;
|
||||
}
|
||||
return domainName;
|
||||
}
|
||||
|
||||
public boolean matches(final String domain) {
|
||||
if (domain == null) {
|
||||
return false;
|
||||
}
|
||||
final String domainRoot = getDomainRoot(domain.startsWith(".") ? domain.substring(1) : domain);
|
||||
return domainRoot == null;
|
||||
}
|
||||
|
||||
}
|
8
core/java/src/org/apache/http/conn/util/package.html
Normal file
8
core/java/src/org/apache/http/conn/util/package.html
Normal file
@ -0,0 +1,8 @@
|
||||
<html><body>
|
||||
<p>
|
||||
From Apache HttpClient 4.4.1.
|
||||
For HostnameVerifier.
|
||||
Small modifications to remove additional Apache dependencies.
|
||||
Apache 2.0 license.
|
||||
</p>
|
||||
</body></html>
|
38
core/java/src/org/apache/http/util/Args.java
Normal file
38
core/java/src/org/apache/http/util/Args.java
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.http.util;
|
||||
|
||||
public class Args {
|
||||
|
||||
public static <T> T notNull(final T argument, final String name) {
|
||||
if (argument == null) {
|
||||
throw new IllegalArgumentException(name + " may not be null");
|
||||
}
|
||||
return argument;
|
||||
}
|
||||
}
|
8
core/java/src/org/apache/http/util/package.html
Normal file
8
core/java/src/org/apache/http/util/package.html
Normal file
@ -0,0 +1,8 @@
|
||||
<html><body>
|
||||
<p>
|
||||
From Apache HttpCore 4.4.1.
|
||||
For HostnameVerifier.
|
||||
Small modifications to remove additional Apache dependencies.
|
||||
Apache 2.0 license.
|
||||
</p>
|
||||
</body></html>
|
Reference in New Issue
Block a user