forked from I2P_Developers/i2p.i2p
Router timestamper:
- Add country-to-continent mapping - Add continent pool.ntp.org zones as first fallback, this will improve time service for countries that don't have a zone - Don't start threads in constructors - Fix logging, better prevention of initialization loops - Log severe errors to wrapper log also continent.txt file from http://dev.maxmind.com/geoip/legacy/codes/country_continent/ Creative Commons Attribution-ShareAlike 3.0 Unported License http://dev.maxmind.com/geoip/legacy/geolite/ Terms already met in LICENSE.txt
This commit is contained in:
@ -1405,6 +1405,7 @@
|
||||
<copy file="installer/resources/countries.txt" todir="pkg-temp/geoip/" />
|
||||
<!-- small enough to include for now -->
|
||||
<copy file="installer/resources/geoipv6.dat.gz" todir="pkg-temp/geoip/" />
|
||||
<copy file="installer/resources/continents.txt" todir="pkg-temp/geoip/" />
|
||||
</target>
|
||||
|
||||
<target name="prepupdateRouter" depends="buildrouter, deletepkg-temp">
|
||||
@ -1422,6 +1423,7 @@
|
||||
<copy file="installer/resources/geoip.txt" todir="pkg-temp/geoip/" />
|
||||
<copy file="installer/resources/geoipv6.dat.gz" todir="pkg-temp/geoip/" />
|
||||
<copy file="installer/resources/countries.txt" todir="pkg-temp/geoip/" />
|
||||
<copy file="installer/resources/continents.txt" todir="pkg-temp/geoip/" />
|
||||
</target>
|
||||
|
||||
<!-- All jetty jars required for update.
|
||||
|
255
installer/resources/continents.txt
Normal file
255
installer/resources/continents.txt
Normal file
@ -0,0 +1,255 @@
|
||||
# From http://dev.maxmind.com/static/csv/codes/country_continent.csv
|
||||
# Fetched 2015-04-21
|
||||
#"iso 3166 country","continent code"
|
||||
A1,--
|
||||
A2,--
|
||||
AD,EU
|
||||
AE,AS
|
||||
AF,AS
|
||||
AG,NA
|
||||
AI,NA
|
||||
AL,EU
|
||||
AM,AS
|
||||
AN,NA
|
||||
AO,AF
|
||||
AP,AS
|
||||
AQ,AN
|
||||
AR,SA
|
||||
AS,OC
|
||||
AT,EU
|
||||
AU,OC
|
||||
AW,NA
|
||||
AX,EU
|
||||
AZ,AS
|
||||
BA,EU
|
||||
BB,NA
|
||||
BD,AS
|
||||
BE,EU
|
||||
BF,AF
|
||||
BG,EU
|
||||
BH,AS
|
||||
BI,AF
|
||||
BJ,AF
|
||||
BL,NA
|
||||
BM,NA
|
||||
BN,AS
|
||||
BO,SA
|
||||
BR,SA
|
||||
BS,NA
|
||||
BT,AS
|
||||
BV,AN
|
||||
BW,AF
|
||||
BY,EU
|
||||
BZ,NA
|
||||
CA,NA
|
||||
CC,AS
|
||||
CD,AF
|
||||
CF,AF
|
||||
CG,AF
|
||||
CH,EU
|
||||
CI,AF
|
||||
CK,OC
|
||||
CL,SA
|
||||
CM,AF
|
||||
CN,AS
|
||||
CO,SA
|
||||
CR,NA
|
||||
CU,NA
|
||||
CV,AF
|
||||
CX,AS
|
||||
CY,AS
|
||||
CZ,EU
|
||||
DE,EU
|
||||
DJ,AF
|
||||
DK,EU
|
||||
DM,NA
|
||||
DO,NA
|
||||
DZ,AF
|
||||
EC,SA
|
||||
EE,EU
|
||||
EG,AF
|
||||
EH,AF
|
||||
ER,AF
|
||||
ES,EU
|
||||
ET,AF
|
||||
EU,EU
|
||||
FI,EU
|
||||
FJ,OC
|
||||
FK,SA
|
||||
FM,OC
|
||||
FO,EU
|
||||
FR,EU
|
||||
FX,EU
|
||||
GA,AF
|
||||
GB,EU
|
||||
GD,NA
|
||||
GE,AS
|
||||
GF,SA
|
||||
GG,EU
|
||||
GH,AF
|
||||
GI,EU
|
||||
GL,NA
|
||||
GM,AF
|
||||
GN,AF
|
||||
GP,NA
|
||||
GQ,AF
|
||||
GR,EU
|
||||
GS,AN
|
||||
GT,NA
|
||||
GU,OC
|
||||
GW,AF
|
||||
GY,SA
|
||||
HK,AS
|
||||
HM,AN
|
||||
HN,NA
|
||||
HR,EU
|
||||
HT,NA
|
||||
HU,EU
|
||||
ID,AS
|
||||
IE,EU
|
||||
IL,AS
|
||||
IM,EU
|
||||
IN,AS
|
||||
IO,AS
|
||||
IQ,AS
|
||||
IR,AS
|
||||
IS,EU
|
||||
IT,EU
|
||||
JE,EU
|
||||
JM,NA
|
||||
JO,AS
|
||||
JP,AS
|
||||
KE,AF
|
||||
KG,AS
|
||||
KH,AS
|
||||
KI,OC
|
||||
KM,AF
|
||||
KN,NA
|
||||
KP,AS
|
||||
KR,AS
|
||||
KW,AS
|
||||
KY,NA
|
||||
KZ,AS
|
||||
LA,AS
|
||||
LB,AS
|
||||
LC,NA
|
||||
LI,EU
|
||||
LK,AS
|
||||
LR,AF
|
||||
LS,AF
|
||||
LT,EU
|
||||
LU,EU
|
||||
LV,EU
|
||||
LY,AF
|
||||
MA,AF
|
||||
MC,EU
|
||||
MD,EU
|
||||
ME,EU
|
||||
MF,NA
|
||||
MG,AF
|
||||
MH,OC
|
||||
MK,EU
|
||||
ML,AF
|
||||
MM,AS
|
||||
MN,AS
|
||||
MO,AS
|
||||
MP,OC
|
||||
MQ,NA
|
||||
MR,AF
|
||||
MS,NA
|
||||
MT,EU
|
||||
MU,AF
|
||||
MV,AS
|
||||
MW,AF
|
||||
MX,NA
|
||||
MY,AS
|
||||
MZ,AF
|
||||
NA,AF
|
||||
NC,OC
|
||||
NE,AF
|
||||
NF,OC
|
||||
NG,AF
|
||||
NI,NA
|
||||
NL,EU
|
||||
NO,EU
|
||||
NP,AS
|
||||
NR,OC
|
||||
NU,OC
|
||||
NZ,OC
|
||||
O1,--
|
||||
OM,AS
|
||||
PA,NA
|
||||
PE,SA
|
||||
PF,OC
|
||||
PG,OC
|
||||
PH,AS
|
||||
PK,AS
|
||||
PL,EU
|
||||
PM,NA
|
||||
PN,OC
|
||||
PR,NA
|
||||
PS,AS
|
||||
PT,EU
|
||||
PW,OC
|
||||
PY,SA
|
||||
QA,AS
|
||||
RE,AF
|
||||
RO,EU
|
||||
RS,EU
|
||||
RU,EU
|
||||
RW,AF
|
||||
SA,AS
|
||||
SB,OC
|
||||
SC,AF
|
||||
SD,AF
|
||||
SE,EU
|
||||
SG,AS
|
||||
SH,AF
|
||||
SI,EU
|
||||
SJ,EU
|
||||
SK,EU
|
||||
SL,AF
|
||||
SM,EU
|
||||
SN,AF
|
||||
SO,AF
|
||||
SR,SA
|
||||
ST,AF
|
||||
SV,NA
|
||||
SY,AS
|
||||
SZ,AF
|
||||
TC,NA
|
||||
TD,AF
|
||||
TF,AN
|
||||
TG,AF
|
||||
TH,AS
|
||||
TJ,AS
|
||||
TK,OC
|
||||
TL,AS
|
||||
TM,AS
|
||||
TN,AF
|
||||
TO,OC
|
||||
TR,EU
|
||||
TT,NA
|
||||
TV,OC
|
||||
TW,AS
|
||||
TZ,AF
|
||||
UA,EU
|
||||
UG,AF
|
||||
UM,OC
|
||||
US,NA
|
||||
UY,SA
|
||||
UZ,AS
|
||||
VA,EU
|
||||
VC,NA
|
||||
VE,SA
|
||||
VG,NA
|
||||
VI,NA
|
||||
VN,AS
|
||||
VU,OC
|
||||
WF,OC
|
||||
WS,OC
|
||||
YE,AS
|
||||
YT,AF
|
||||
ZA,AF
|
||||
ZM,AF
|
||||
ZW,AF
|
@ -40,7 +40,7 @@ public class RouterClock extends Clock {
|
||||
/** use system time for this */
|
||||
private long _lastChanged;
|
||||
private int _lastStratum;
|
||||
private final Timestamper _timeStamper;
|
||||
private final RouterTimestamper _timeStamper;
|
||||
|
||||
/**
|
||||
* If the system clock shifts by this much,
|
||||
@ -53,6 +53,9 @@ public class RouterClock extends Clock {
|
||||
private final Set<ClockShiftListener> _shiftListeners;
|
||||
private volatile long _lastShiftNanos;
|
||||
|
||||
/**
|
||||
* Does not start. Caller MUST call start()
|
||||
*/
|
||||
public RouterClock(RouterContext context) {
|
||||
super(context);
|
||||
_lastStratum = WORST_STRATUM;
|
||||
@ -62,6 +65,14 @@ public class RouterClock extends Clock {
|
||||
_timeStamper = new RouterTimestamper(context, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cannot be stopped, but RouterTimestamper registers a shutdown task.
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void start() {
|
||||
_timeStamper.startTimestamper();
|
||||
}
|
||||
|
||||
/**
|
||||
* The RouterTimestamper
|
||||
*/
|
||||
|
@ -483,8 +483,11 @@ public class RouterContext extends I2PAppContext {
|
||||
@Override
|
||||
protected void initializeClock() {
|
||||
synchronized (_lock1) {
|
||||
if (_clock == null)
|
||||
_clock = new RouterClock(this);
|
||||
if (_clock == null) {
|
||||
RouterClock rc = new RouterClock(this);
|
||||
rc.start();
|
||||
_clock = rc;
|
||||
}
|
||||
_clockInitialized = true;
|
||||
}
|
||||
}
|
||||
|
@ -20,9 +20,10 @@ import net.i2p.util.Log;
|
||||
*/
|
||||
public class RouterTimestamper extends Timestamper {
|
||||
private final I2PAppContext _context;
|
||||
// warning, may be null
|
||||
private Log _log;
|
||||
private final List<String> _servers;
|
||||
private List<String> _priorityServers;
|
||||
private List<List<String>> _priorityServers;
|
||||
private final List<UpdateListener> _listeners;
|
||||
private int _queryFrequency;
|
||||
private int _concurringServers;
|
||||
@ -33,11 +34,12 @@ public class RouterTimestamper extends Timestamper {
|
||||
private boolean _wellSynced;
|
||||
private volatile boolean _isRunning;
|
||||
private Thread _timestamperThread;
|
||||
private final Zones _zones;
|
||||
|
||||
private static final int MIN_QUERY_FREQUENCY = 5*60*1000;
|
||||
private static final int DEFAULT_QUERY_FREQUENCY = 11*60*1000;
|
||||
private static final String DEFAULT_SERVER_LIST = "0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org";
|
||||
private static final String DEFAULT_DISABLED = "true";
|
||||
private static final boolean DEFAULT_DISABLED = true;
|
||||
/** how many times do we have to query if we are changing the clock? */
|
||||
private static final int DEFAULT_CONCURRING_SERVERS = 3;
|
||||
private static final int MAX_CONSECUTIVE_FAILS = 10;
|
||||
@ -51,13 +53,23 @@ public class RouterTimestamper extends Timestamper {
|
||||
/** if different SNTP servers differ by more than 10s, someone is b0rked */
|
||||
private static final int MAX_VARIANCE = 10*1000;
|
||||
|
||||
/**
|
||||
* Does not start. Caller MUST call startTimestamper()
|
||||
*/
|
||||
public RouterTimestamper(I2PAppContext ctx) {
|
||||
this(ctx, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not start. Caller MUST call startTimestamper()
|
||||
*/
|
||||
public RouterTimestamper(I2PAppContext ctx, UpdateListener lsnr) {
|
||||
this(ctx, lsnr, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not start. Caller MUST call startTimestamper()
|
||||
*/
|
||||
public RouterTimestamper(I2PAppContext ctx, UpdateListener lsnr, boolean daemon) {
|
||||
super();
|
||||
// moved here to prevent problems with synchronized statements.
|
||||
@ -71,15 +83,17 @@ public class RouterTimestamper extends Timestamper {
|
||||
// This means we no longer check every 5 minutes to see if we got enabled,
|
||||
// so the property must be set at startup.
|
||||
// We still need to be instantiated since the router calls clock().getTimestamper().waitForInitialization()
|
||||
String disabled = ctx.getProperty(PROP_DISABLED, DEFAULT_DISABLED);
|
||||
if (Boolean.parseBoolean(disabled)) {
|
||||
_disabled = ctx.getProperty(PROP_DISABLED, DEFAULT_DISABLED);
|
||||
if (_disabled) {
|
||||
_initialized = true;
|
||||
_zones = null;
|
||||
System.out.println("Warning: NTP is disabled");
|
||||
return;
|
||||
}
|
||||
if (lsnr != null)
|
||||
_listeners.add(lsnr);
|
||||
_zones = new Zones(ctx);
|
||||
updateConfig();
|
||||
startTimestamper();
|
||||
}
|
||||
|
||||
public int getServerCount() {
|
||||
@ -110,7 +124,9 @@ public class RouterTimestamper extends Timestamper {
|
||||
return _listeners.get(index);
|
||||
}
|
||||
|
||||
private void startTimestamper() {
|
||||
public void startTimestamper() {
|
||||
if (_disabled || _initialized)
|
||||
return;
|
||||
_timestamperThread = new I2PThread(this, "Timestamper", _daemon);
|
||||
_timestamperThread.setPriority(I2PThread.MIN_PRIORITY);
|
||||
_isRunning = true;
|
||||
@ -149,48 +165,72 @@ public class RouterTimestamper extends Timestamper {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
|
||||
_log = _context.logManager().getLog(Timestamper.class);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Starting timestamper");
|
||||
boolean lastFailed = false;
|
||||
try {
|
||||
while (_isRunning) {
|
||||
// NOTE: _log is null the first time through, to prevent problems and stack overflows
|
||||
updateConfig();
|
||||
if (!_disabled) {
|
||||
// first the servers for our country, if we know what country we're in...
|
||||
// first the servers for our country and continent, we know what country we're in...
|
||||
if (_priorityServers != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Querying servers " + _priorityServers);
|
||||
try {
|
||||
lastFailed = !queryTime(_priorityServers.toArray(new String[_priorityServers.size()]));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
if ( (!lastFailed) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("Unable to reach country-specific NTP servers");
|
||||
lastFailed = true;
|
||||
for (List<String> servers : _priorityServers) {
|
||||
if (_log != null && _log.shouldDebug())
|
||||
_log.debug("Querying servers " + servers);
|
||||
try {
|
||||
lastFailed = !queryTime(servers.toArray(new String[servers.size()]));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
if (!lastFailed && _log != null && _log.shouldWarn())
|
||||
_log.warn("Unable to reach any regional NTP servers: " + servers);
|
||||
lastFailed = true;
|
||||
}
|
||||
if (!lastFailed)
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ... and then the global list, if that failed
|
||||
if (_priorityServers == null || lastFailed) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
if (_log != null && _log.shouldDebug())
|
||||
_log.debug("Querying servers " + _servers);
|
||||
try {
|
||||
lastFailed = !queryTime(_servers.toArray(new String[_servers.size()]));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
if ( (!_initialized) && (_log.shouldLog(Log.ERROR)) ) {
|
||||
List<String> all = new ArrayList<String>();
|
||||
if (_priorityServers != null)
|
||||
all.addAll(_priorityServers);
|
||||
all.addAll(_servers);
|
||||
_log.error("Unable to reach any of the NTP servers " + all + " - network disconnected? Or set time.sntpServerList=myserver1.com,myserver2.com in advanced configuration.");
|
||||
}
|
||||
lastFailed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
boolean wasInitialized = _initialized;
|
||||
if (!wasInitialized)
|
||||
_initialized = true;
|
||||
synchronized (this) { notifyAll(); }
|
||||
if (!wasInitialized) {
|
||||
// let the log manager get initialized
|
||||
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
|
||||
// NOW we set up logging
|
||||
_log = _context.logManager().getLog(RouterTimestamper.class);
|
||||
if (lastFailed) {
|
||||
List<String> all = new ArrayList<String>(9);
|
||||
if (_priorityServers != null) {
|
||||
for (List<String> servers : _priorityServers) {
|
||||
all.addAll(servers);
|
||||
}
|
||||
}
|
||||
all.addAll(_servers);
|
||||
String msg = "Unable to reach any of the NTP servers " + all +
|
||||
" - network disconnected? Or set time.sntpServerList=myserver1.com,myserver2.com in advanced configuration.";
|
||||
_log.logAlways(Log.WARN, msg);
|
||||
System.out.println("Warning: " + msg);
|
||||
} else if (_log.shouldDebug()) {
|
||||
_log.debug("NTP initialization successful");
|
||||
int i = 1;
|
||||
if (_priorityServers != null) {
|
||||
for (List<String> servers : _priorityServers) {
|
||||
_log.debug("NTP Server list " + (i++) + ": " + servers);
|
||||
}
|
||||
}
|
||||
_log.debug("NTP Server list " + i + ": " + _servers);
|
||||
}
|
||||
}
|
||||
long sleepTime;
|
||||
if (lastFailed) {
|
||||
if (++_consecutiveFails >= MAX_CONSECUTIVE_FAILS)
|
||||
@ -206,8 +246,10 @@ public class RouterTimestamper extends Timestamper {
|
||||
try { Thread.sleep(sleepTime); } catch (InterruptedException ie) {}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Timestamper died!", t);
|
||||
synchronized (this) { notifyAll(); }
|
||||
if (_log != null)
|
||||
_log.log(Log.CRIT, "Timestamper died!", t);
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,7 +274,7 @@ public class RouterTimestamper extends Timestamper {
|
||||
found[i] = delta;
|
||||
if (i == 0) {
|
||||
if (Math.abs(delta) < MAX_VARIANCE) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
if (_log != null && _log.shouldInfo())
|
||||
_log.info("a single SNTP query was within the tolerance (" + delta + "ms)");
|
||||
// If less than a half second on the first try, we're in good shape
|
||||
_wellSynced = Math.abs(delta) < 500;
|
||||
@ -243,7 +285,7 @@ public class RouterTimestamper extends Timestamper {
|
||||
}
|
||||
} else {
|
||||
if (Math.abs(delta - expectedDelta) > MAX_VARIANCE) {
|
||||
if (_log.shouldLog(Log.ERROR)) {
|
||||
if (_log != null && _log.shouldError()) {
|
||||
StringBuilder err = new StringBuilder(96);
|
||||
err.append("SNTP client variance exceeded at query ").append(i);
|
||||
err.append(". expected = ");
|
||||
@ -260,7 +302,7 @@ public class RouterTimestamper extends Timestamper {
|
||||
}
|
||||
}
|
||||
stampTime(now, stratum);
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
if (_log != null && _log.shouldDebug()) {
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
buf.append("Deltas: ");
|
||||
for (int i = 0; i < found.length; i++)
|
||||
@ -280,13 +322,13 @@ public class RouterTimestamper extends Timestamper {
|
||||
for (UpdateListener lsnr : _listeners) {
|
||||
lsnr.setNow(now, stratum);
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
if (_log != null && _log.shouldDebug())
|
||||
_log.debug("Stamped the time as " + now + " (delta=" + (now-before) + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload all the config elements from the appContext
|
||||
*
|
||||
* Reload all the config elements from the appContext.
|
||||
* No logging allowed here
|
||||
*/
|
||||
private void updateConfig() {
|
||||
String serverList = _context.getProperty(PROP_SERVER_LIST);
|
||||
@ -298,10 +340,22 @@ public class RouterTimestamper extends Timestamper {
|
||||
if (country != null)
|
||||
country = country.toLowerCase(Locale.US);
|
||||
}
|
||||
if (country != null && country.length() > 0) {
|
||||
_priorityServers = new ArrayList<String>(3);
|
||||
for (int i = 0; i < 3; i++)
|
||||
_priorityServers.add(i + "." + country + ".pool.ntp.org");
|
||||
if (country != null && country.length() > 0 &&
|
||||
!country.equals("a1") && !country.equals("a2")) {
|
||||
_priorityServers = new ArrayList<List<String>>(2);
|
||||
List<String> p1 = new ArrayList<String>(3);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
p1.add(i + "." + country + ".pool.ntp.org");
|
||||
}
|
||||
_priorityServers.add(p1);
|
||||
String zone = _zones.getZone(country);
|
||||
if (zone != null) {
|
||||
List<String> p2 = new ArrayList<String>(3);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
p2.add(i + "." + zone + ".pool.ntp.org");
|
||||
}
|
||||
_priorityServers.add(p2);
|
||||
}
|
||||
} else {
|
||||
_priorityServers = null;
|
||||
}
|
||||
@ -320,8 +374,7 @@ public class RouterTimestamper extends Timestamper {
|
||||
_queryFrequency = Math.max(MIN_QUERY_FREQUENCY,
|
||||
_context.getProperty(PROP_QUERY_FREQUENCY, DEFAULT_QUERY_FREQUENCY));
|
||||
|
||||
String disabled = _context.getProperty(PROP_DISABLED, DEFAULT_DISABLED);
|
||||
_disabled = Boolean.parseBoolean(disabled);
|
||||
_disabled = _context.getProperty(PROP_DISABLED, DEFAULT_DISABLED);
|
||||
|
||||
_concurringServers = Math.min(4, Math.max(1,
|
||||
_context.getProperty(PROP_CONCURRING_SERVERS, DEFAULT_CONCURRING_SERVERS)));
|
||||
|
135
router/java/src/net/i2p/router/time/Zones.java
Normal file
135
router/java/src/net/i2p/router/time/Zones.java
Normal file
@ -0,0 +1,135 @@
|
||||
package net.i2p.router.time;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.router.transport.GeoIP;
|
||||
|
||||
/**
|
||||
* Country to continent mapping for NTP.
|
||||
* @since 0.9.20
|
||||
*/
|
||||
class Zones {
|
||||
|
||||
private final I2PAppContext _context;
|
||||
// can't log here, called from RouterClock constructor, stack overflow
|
||||
//private final Log _log;
|
||||
|
||||
// lower case country to NTP region
|
||||
private final Map<String, String> _countryToZone;
|
||||
// upper case continent to NTP region
|
||||
private final Map<String, String> _continentToZone;
|
||||
|
||||
private static final String CONTINENT_FILE_DEFAULT = "continents.txt";
|
||||
|
||||
/**
|
||||
* ref: http://dev.maxmind.com/geoip/legacy/codes/country_continent/
|
||||
* ref: http://www.pool.ntp.org/zone/@
|
||||
*/
|
||||
private static final String[] ZONES = {
|
||||
// not an NTP zone
|
||||
//"--", "anonymous-proxy",
|
||||
"AF", "africa",
|
||||
// not an NTP zone
|
||||
//"AN", "antarctica",
|
||||
"AS", "asia",
|
||||
"EU", "europe",
|
||||
"NA", "north-america",
|
||||
"OC", "oceania",
|
||||
"SA", "south-america"
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads in the file in the constructor,
|
||||
* so hold onto this.
|
||||
*/
|
||||
public Zones(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
//_log = ctx.logManager().getLog(Zones.class);
|
||||
_countryToZone = new HashMap<String, String>(256);
|
||||
_continentToZone = new HashMap<String, String>(8);
|
||||
for (int i = 0; i < ZONES.length; i += 2) {
|
||||
_continentToZone.put(ZONES[i], ZONES[i+1]);
|
||||
}
|
||||
readContinentFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the NTP zone for a country
|
||||
*
|
||||
* @param country non-null, two letter code, case-independent
|
||||
* @return lower-case NTP zone, e.g. "africa", or null
|
||||
*/
|
||||
public String getZone (String country) {
|
||||
return _countryToZone.get(country.toLowerCase(Locale.US));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read in and parse the continent file.
|
||||
* The file need not be sorted.
|
||||
*
|
||||
* Format:
|
||||
* #comment (# must be in column 1)
|
||||
* country code,continent code
|
||||
*
|
||||
* Example:
|
||||
* US,NA
|
||||
*
|
||||
* Modified from GeoIP.readCountryFile()
|
||||
* ref: http://dev.maxmind.com/geoip/legacy/codes/country_continent/
|
||||
*/
|
||||
private void readContinentFile() {
|
||||
String geoDir = _context.getProperty(GeoIP.PROP_GEOIP_DIR, GeoIP.GEOIP_DIR_DEFAULT);
|
||||
File geoFile = new File(geoDir);
|
||||
if (!geoFile.isAbsolute())
|
||||
geoFile = new File(_context.getBaseDir(), geoDir);
|
||||
geoFile = new File(geoFile, CONTINENT_FILE_DEFAULT);
|
||||
if (!geoFile.exists()) {
|
||||
//if (_log.shouldWarn())
|
||||
// _log.warn("Continent file not found: " + geoFile.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
br = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(geoFile), "UTF-8"));
|
||||
String line = null;
|
||||
while ((line = br.readLine()) != null) {
|
||||
try {
|
||||
if (line.charAt(0) == '#')
|
||||
continue;
|
||||
String[] s = line.split(",");
|
||||
String ucContinent = s[1].toUpperCase(Locale.US).trim();
|
||||
String zone = _continentToZone.get(ucContinent);
|
||||
if (zone == null)
|
||||
continue;
|
||||
String lcCountry = s[0].toLowerCase(Locale.US).trim();
|
||||
_countryToZone.put(lcCountry, zone);
|
||||
//if (_log.shouldDebug())
|
||||
// _log.debug("Country " + lcCountry + " is in " + zone);
|
||||
} catch (IndexOutOfBoundsException ioobe) {}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("Error reading the continent file " + geoFile.getAbsolutePath());
|
||||
} finally {
|
||||
if (br != null) try { br.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/****
|
||||
public static void main(String args[]) {
|
||||
Zones z = new Zones(I2PAppContext.getGlobalContext());
|
||||
String tests[] = {"us", "US", "nz", "fr", "vU", "br", "cn", "ao", "A1", "foo" };
|
||||
for (int i = 0; i < tests.length; i++) {
|
||||
System.out.println(tests[i] + " : " + z.getZone(tests[i]));
|
||||
}
|
||||
}
|
||||
****/
|
||||
}
|
@ -73,7 +73,7 @@ public class GeoIP {
|
||||
|
||||
static final String PROP_GEOIP_ENABLED = "routerconsole.geoip.enable";
|
||||
public static final String PROP_GEOIP_DIR = "geoip.dir";
|
||||
static final String GEOIP_DIR_DEFAULT = "geoip";
|
||||
public static final String GEOIP_DIR_DEFAULT = "geoip";
|
||||
static final String GEOIP_FILE_DEFAULT = "geoip.txt";
|
||||
static final String COUNTRY_FILE_DEFAULT = "countries.txt";
|
||||
public static final String PROP_IP_COUNTRY = "i2np.lastCountry";
|
||||
|
Reference in New Issue
Block a user