Add geoip support and flag icons

This commit is contained in:
zzz
2009-05-19 18:07:19 +00:00
parent cb5390b2fb
commit 3ba43a77f4
260 changed files with 92434 additions and 45 deletions

View File

@ -59,6 +59,9 @@ public abstract class CommSystemFacade implements Service {
public boolean wasUnreachable(Hash dest) { return false; }
public boolean isEstablished(Hash dest) { return false; }
public byte[] getIP(Hash dest) { return null; }
public void queueLookup(byte[] ip) {}
public String getCountry(Hash peer) { return null; }
public String renderPeerHTML(Hash peer) { return null; }
/**
* Tell other transports our address changed

View File

@ -97,19 +97,9 @@ class ProfileOrganizerRenderer {
buf.append("<tr><td colspan=\"7\"><hr /></td></tr>\n");
prevTier = tier;
buf.append("<tr>");
buf.append("<td><code>");
if (prof.getIsFailing()) {
buf.append("<font color=\"red\">-- ").append(peer.toBase64().substring(0,6)).append("</font>");
} else {
if (prof.getIsActive()) {
buf.append("<font color=\"blue\">++ ").append(peer.toBase64().substring(0,6)).append("</font>");
} else {
buf.append("&nbsp;&nbsp;&nbsp;").append(peer.toBase64().substring(0,6));
}
}
buf.append("</code></td>");
buf.append("<td>");
buf.append("<tr><td>");
buf.append(_context.commSystem().renderPeerHTML(peer));
buf.append("</td><td>");
switch (tier) {
case 1: buf.append("Fast, High Capacity"); break;
@ -159,10 +149,7 @@ class ProfileOrganizerRenderer {
buf.append(' ').append(fails).append('/').append(total).append(" Test Fails");
}
buf.append("&nbsp</td>");
//buf.append("<td><a href=\"/profile/").append(prof.getPeer().toBase64().substring(0, 32)).append("\">profile.txt</a> ");
//buf.append(" <a href=\"#").append(prof.getPeer().toBase64().substring(0, 32)).append("\">netDb</a></td>");
buf.append("<td nowrap><a href=\"netdb.jsp?r=").append(peer.toBase64().substring(0,6)).append("\">netDb</a>");
buf.append("/<a href=\"dumpprofile.jsp?peer=").append(peer.toBase64().substring(0,6)).append("\">profile</a>");
buf.append("<td nowrap><a href=\"dumpprofile.jsp?peer=").append(peer.toBase64().substring(0,6)).append("\">profile</a>");
buf.append("/<a href=\"configpeer.jsp?peer=").append(peer.toBase64()).append("\">+-</a></td>\n");
buf.append("</tr>");
}
@ -192,17 +179,9 @@ class ProfileOrganizerRenderer {
PeerProfile prof = (PeerProfile)iter.next();
Hash peer = prof.getPeer();
buf.append("<tr>");
buf.append("<td><code>");
if (prof.getIsFailing()) {
buf.append("<font color=\"red\">-- ").append(peer.toBase64().substring(0,6)).append("</font>");
} else {
if (prof.getIsActive()) {
buf.append("<font color=\"blue\">++ ").append(peer.toBase64().substring(0,6)).append("</font>");
} else {
buf.append("&nbsp;&nbsp;&nbsp;").append(peer.toBase64().substring(0,6));
}
}
buf.append("<tr><td>");
buf.append(_context.commSystem().renderPeerHTML(peer));
buf.append("</td>");
RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer);
if (info != null)
buf.append("<td align=\"center\">" + info.getCapabilities() + "</td>");

View File

@ -29,16 +29,20 @@ import net.i2p.router.transport.ntcp.NTCPTransport;
import net.i2p.router.transport.udp.UDPAddress;
import net.i2p.router.transport.udp.UDPTransport;
import net.i2p.util.Log;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer;
public class CommSystemFacadeImpl extends CommSystemFacade {
private Log _log;
private RouterContext _context;
private TransportManager _manager;
private GeoIP _geoIP;
public CommSystemFacadeImpl(RouterContext context) {
_context = context;
_log = _context.logManager().getLog(CommSystemFacadeImpl.class);
_manager = null;
startGeoIP();
}
public void startup() {
@ -348,4 +352,56 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
return;
}
private static final int START_DELAY = 5*60*1000;
private static final int LOOKUP_TIME = 30*60*1000;
private void startGeoIP() {
_geoIP = new GeoIP(_context);
SimpleScheduler.getInstance().addPeriodicEvent(new Lookup(), START_DELAY, LOOKUP_TIME);
}
private class Lookup implements SimpleTimer.TimedEvent {
public void timeReached() {
_geoIP.blockingLookup();
}
}
public void queueLookup(byte[] ip) {
_geoIP.add(ip);
}
/**
* Right now this only uses the transport IP,
* which is present only after you've connected to the peer.
* We could also check the IP in the netDb entry...
*/
public String getCountry(Hash peer) {
byte[] ip = TransportImpl.getIP(peer);
if (ip == null)
return null;
return _geoIP.get(ip);
}
public String renderPeerHTML(Hash peer) {
String h = peer.toBase64().substring(0, 4);
StringBuffer buf = new StringBuffer(128);
buf.append("<tt><font size=\"+1\">");
boolean found = _context.netDb().lookupRouterInfoLocally(peer) != null;
if (found)
buf.append("<a title=\"NetDb entry\" href=\"netdb.jsp?r=").append(h).append("\">");
buf.append(h);
if (found)
buf.append("</a>");
buf.append("</font></tt>");
String c = getCountry(peer);
if (c != null) {
buf.append(" <img alt=\"").append(c.toUpperCase()).append("\" title=\"");
String n = _geoIP.fullName(c);
if (n != null)
buf.append(n);
else
buf.append(c);
buf.append("\" src=\"/flags.jsp?c=").append(c).append("\">");
}
return buf.toString();
}
}

View File

@ -0,0 +1,311 @@
package net.i2p.router.transport;
/*
* free (adj.): unencumbered; not under the control of others
* Use at your own risk.
*/
import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.Log;
/**
* Manage geoip lookup in a file with the Tor geoip format.
*
* The lookup is expensive, so a lookup is queued with add().
* The actual lookup of multiple IPs is fired with lookup().
* To get a country for an IP, use get() which returns a lower-case,
* generally two-letter country code or null.
*
* Everything here uses longs, since Java is signed-only, the file is
* sorted by unsigned, and we don't store the table in memory
* (unlike in Blocklist.java, where it's in-memory so we want to be
* space-efficient)
*
* @author zzz
*/
public class GeoIP {
private Log _log;
private I2PAppContext _context;
private final Map<String, String> _codeToName;
private final Map<Long, String> _IPToCountry;
private final Set<Long> _pendingSearch;
private final Set<Long> _notFound;
private final AtomicBoolean _lock;
public GeoIP(I2PAppContext context) {
_context = context;
_log = context.logManager().getLog(GeoIP.class);
_codeToName = new ConcurrentHashMap();
_IPToCountry = new ConcurrentHashMap();
_pendingSearch = new ConcurrentHashSet();
_notFound = new ConcurrentHashSet();
_lock = new AtomicBoolean();
readCountryFile();
}
static final String PROP_GEOIP_ENABLED = "routerconsole.geoip.enable";
static final String GEOIP_DIR_DEFAULT = "geoip";
static final String GEOIP_FILE_DEFAULT = "geoip.txt";
static final String COUNTRY_FILE_DEFAULT = "countries.txt";
/**
* Fire off a thread to lookup all pending IPs.
* There is no indication of completion.
* Results will be added to the table and available via get() after completion.
*/
/******
public void lookup() {
if (! Boolean.valueOf(_context.getProperty(PROP_GEOIP_ENABLED, "true")).booleanValue()) {
_pendingSearch.clear();
return;
}
Thread t = new Thread(new LookupJob());
t.start();
}
******/
/**
* Blocking lookup of all pending IPs.
* Results will be added to the table and available via get() after completion.
*/
public void blockingLookup() {
if (! Boolean.valueOf(_context.getProperty(PROP_GEOIP_ENABLED, "true")).booleanValue()) {
_pendingSearch.clear();
return;
}
LookupJob j = new LookupJob();
j.run();
}
private class LookupJob implements Runnable {
public void run() {
if (_lock.getAndSet(true))
return;
Long[] search = _pendingSearch.toArray(new Long[_pendingSearch.size()]);
if (search.length <= 0)
return;
_pendingSearch.clear();
Arrays.sort(search);
String[] countries = readGeoIPFile(search);
for (int i = 0; i < countries.length; i++) {
if (countries[i] != null)
_IPToCountry.put(search[i], countries[i]);
else
_notFound.add(search[i]);
}
_lock.set(false);
}
}
/**
* Read in and parse the country file.
* The geoip file need not be sorted.
*
* Acceptable formats (IPV4 only):
* #comment (# must be in column 1)
* code,full name
*
* Example:
* US,UNITED STATES
*
* To create:
* wget http://ip-to-country.webhosting.info/downloads/ip-to-country.csv.zip
* unzip ip-to-country.csv.zip
* cut -d, -f3,5 < ip-to-country.csv|sed 's/"//g' | sort | uniq > countries.txt
*
*/
private void readCountryFile() {
File GeoFile = new File(GEOIP_DIR_DEFAULT, COUNTRY_FILE_DEFAULT);
if (GeoFile == null || (!GeoFile.exists())) {
if (_log.shouldLog(Log.WARN))
_log.warn("Country file not found: " + GeoFile.getAbsolutePath());
return;
}
FileInputStream in = null;
try {
in = new FileInputStream(GeoFile);
StringBuffer buf = new StringBuffer(128);
while (DataHelper.readLine(in, buf)) {
try {
if (buf.charAt(0) == '#') {
buf.setLength(0);
continue;
}
String[] s = buf.toString().split(",");
// todo convert name to mixed upper/lower case
_codeToName.put(s[0].toLowerCase(), s[1]);
} catch (IndexOutOfBoundsException ioobe) {
}
buf.setLength(0);
}
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error reading the Country File", ioe);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
}
/**
* Read in and parse the geoip file.
* The geoip file must be sorted, and may not contain overlapping entries.
*
* Acceptable formats (IPV4 only):
* #comment (# must be in column 1)
* integer IP,integer IP, country code
*
* Example:
* 121195296,121195327,IT
*
* This is identical to the Tor geoip file, which can be found in
* src/config/geoip in their distribution, or /usr/local/lib/share/tor/geoip
* in their installation.
* Thanks to Tor for finding a source for the data, and the format script.
*
* To create:
* wget http://ip-to-country.webhosting.info/downloads/ip-to-country.csv.zip
* unzip ip-to-country.csv.zip
* cut -d, -f0-3 < ip-to-country.csv|sed 's/"//g' > geoip.txt
*
* @param search a sorted map of IPs to search, with null values
* returns the map with values entered
*
*/
private String[] readGeoIPFile(Long[] search) {
File GeoFile = new File(GEOIP_DIR_DEFAULT, GEOIP_FILE_DEFAULT);
if (GeoFile == null || (!GeoFile.exists())) {
if (_log.shouldLog(Log.WARN))
_log.warn("GeoIP file not found: " + GeoFile.getAbsolutePath());
return new String[0];
}
String[] rv = new String[search.length];
int idx = 0;
long start = _context.clock().now();
FileInputStream in = null;
try {
in = new FileInputStream(GeoFile);
StringBuffer buf = new StringBuffer(128);
while (DataHelper.readLine(in, buf) && idx < search.length) {
try {
if (buf.charAt(0) == '#') {
buf.setLength(0);
continue;
}
String[] s = buf.toString().split(",");
long ip1 = Long.parseLong(s[0]);
long ip2 = Long.parseLong(s[1]);
while (search[idx].longValue() < ip1 && idx < search.length) {
idx++;
}
while (search[idx].longValue() >= ip1 && search[idx].longValue() <= ip2 && idx < search.length) {
rv[idx++] = s[2].toLowerCase();
}
} catch (IndexOutOfBoundsException ioobe) {
} catch (NumberFormatException nfe) {
}
buf.setLength(0);
}
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error reading the GeoFile", ioe);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
if (_log.shouldLog(Log.WARN)) {
_log.warn("GeoIP processing finished, time: " + (_context.clock().now() - start));
}
return rv;
}
/**
* Add to the list needing lookup
*/
public void add(String ip) {
InetAddress pi;
try {
pi = InetAddress.getByName(ip);
} catch (UnknownHostException uhe) {
return;
}
if (pi == null) return;
byte[] pib = pi.getAddress();
add(pib);
}
public void add(byte ip[]) {
if (ip.length != 4)
return;
add(toLong(ip));
}
private void add(long ip) {
Long li = Long.valueOf(ip);
if (!(_IPToCountry.containsKey(li) || _notFound.contains(li)))
_pendingSearch.add(li);
}
/**
* Get the country for an IP
* @return lower-case code, generally two letters.
*/
public String get(String ip) {
InetAddress pi;
try {
pi = InetAddress.getByName(ip);
} catch (UnknownHostException uhe) {
return null;
}
if (pi == null) return null;
byte[] pib = pi.getAddress();
return get(pib);
}
public String get(byte ip[]) {
if (ip.length != 4)
return null;
return get(toLong(ip));
}
private String get(long ip) {
return _IPToCountry.get(Long.valueOf(ip));
}
private static long toLong(byte ip[]) {
int rv = 0;
for (int i = 0; i < 4; i++)
rv |= (ip[i] & 0xff) << ((3-i)*8);
return ((long) rv) & 0xffffffffl;
}
public String fullName(String code) {
return _codeToName.get(code);
}
public static void main(String args[]) {
GeoIP g = new GeoIP(new I2PAppContext());
String tests[] = {"0.0.0.0", "0.0.0.1", "0.0.0.2", "0.0.0.255", "1.0.0.0",
"94.3.3.3", "77.1.2.3", "127.0.0.0", "127.127.127.127", "128.0.0.0",
"89.8.9.3", "72.5.6.8", "217.4.9.7", "175.107.027.107", "135.6.5.2",
"129.1.2.3", "255.255.255.254", "255.255.255.255"};
for (int i = 0; i < tests.length; i++)
g.add(tests[i]);
g.blockingLookup();
for (int i = 0; i < tests.length; i++)
System.out.println(tests[i] + " : " + g.get(tests[i]));
}
}

View File

@ -538,8 +538,9 @@ public abstract class TransportImpl implements Transport {
_log.warn(this.getStyle() + " setting wasUnreachable to " + yes + " for " + peer);
}
public /* static */ void setIP(Hash peer, byte[] ip) {
public void setIP(Hash peer, byte[] ip) {
_IPMap.put(peer, ip);
_context.commSystem().queueLookup(ip);
}
public static byte[] getIP(Hash peer) {

View File

@ -734,8 +734,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
}
}
private static String netDbLink(Hash peer) {
String h = peer.toBase64().substring(0, 4);
return "<a href=\"netdb.jsp?r=" + h + "\">" + h + "</a>";
private String netDbLink(Hash peer) {
return _context.commSystem().renderPeerHTML(peer);
}
}