package net.i2p.router.web; /* * free (adj.): unencumbered; not under the control of others * Written by jrandom in 2003 and released into the public domain * with no warranty of any kind, either expressed or implied. * It probably won't make your computer catch on fire, or eat * your children, but it might. Use at your own risk. * */ import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; import net.i2p.crypto.TrustedUpdate; import net.i2p.data.DataHelper; import net.i2p.data.Destination; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.RouterAddress; import net.i2p.data.RouterInfo; import net.i2p.router.RouterContext; import net.i2p.router.TunnelPoolSettings; import net.i2p.util.ObjectCounter; public class NetDbRenderer { private RouterContext _context; public NetDbRenderer (RouterContext ctx) { _context = ctx; } private class LeaseSetComparator implements Comparator { public int compare(Object l, Object r) { Destination dl = ((LeaseSet)l).getDestination(); Destination dr = ((LeaseSet)r).getDestination(); boolean locall = _context.clientManager().isLocal(dl); boolean localr = _context.clientManager().isLocal(dr); if (locall && !localr) return -1; if (localr && !locall) return 1; return dl.calculateHash().toBase64().compareTo(dr.calculateHash().toBase64()); } } private static class RouterInfoComparator implements Comparator { public int compare(Object l, Object r) { return ((RouterInfo)l).getIdentity().getHash().toBase64().compareTo(((RouterInfo)r).getIdentity().getHash().toBase64()); } } public void renderRouterInfoHTML(Writer out, String routerPrefix) throws IOException { StringBuilder buf = new StringBuilder(4*1024); buf.append("<h2>" + _("Network Database RouterInfo Lookup") + "</h2>\n"); if (".".equals(routerPrefix)) { renderRouterInfo(buf, _context.router().getRouterInfo(), true, true); } else { boolean notFound = true; Set routers = _context.netDb().getRouters(); for (Iterator iter = routers.iterator(); iter.hasNext(); ) { RouterInfo ri = (RouterInfo)iter.next(); Hash key = ri.getIdentity().getHash(); if (key.toBase64().startsWith(routerPrefix)) { renderRouterInfo(buf, ri, false, true); notFound = false; } } if (notFound) buf.append(_("Router") + ' ').append(routerPrefix).append(' ' + _("not found in network database") ); } out.write(buf.toString()); out.flush(); } public void renderLeaseSetHTML(Writer out) throws IOException { StringBuilder buf = new StringBuilder(4*1024); buf.append("<h2>" + _("Network Database Contents") + "</h2>\n"); buf.append("<a href=\"netdb.jsp\">" + _("View RouterInfo") + "</a>"); buf.append("<h3>").append(_("LeaseSets")).append("</h3>\n"); Set leases = new TreeSet(new LeaseSetComparator()); leases.addAll(_context.netDb().getLeases()); long now = _context.clock().now(); for (Iterator iter = leases.iterator(); iter.hasNext(); ) { LeaseSet ls = (LeaseSet)iter.next(); Destination dest = ls.getDestination(); Hash key = dest.calculateHash(); buf.append("<b>").append(_("LeaseSet")).append(": ").append(key.toBase64()); if (_context.clientManager().isLocal(dest)) { buf.append(" (<a href=\"tunnels.jsp#" + key.toBase64().substring(0,4) + "\">" + _("Local") + "</a> "); if (! _context.clientManager().shouldPublishLeaseSet(key)) buf.append(_("Unpublished") + ' '); buf.append(_("Destination") + ' '); TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(key); if (in != null && in.getDestinationNickname() != null) buf.append(in.getDestinationNickname()); else buf.append(dest.toBase64().substring(0, 6)); } else { buf.append(" (" + _("Destination") + ' '); String host = _context.namingService().reverseLookup(dest); if (host != null) buf.append(host); else buf.append(dest.toBase64().substring(0, 6)); } buf.append(")</b><br>\n"); long exp = ls.getEarliestLeaseDate()-now; if (exp > 0) buf.append(_("Expires in {0}", DataHelper.formatDuration(exp))).append("<br>\n"); else buf.append(_("Expired {0} ago", DataHelper.formatDuration(0-exp))).append("<br>\n"); for (int i = 0; i < ls.getLeaseCount(); i++) { buf.append(_("Lease")).append(' ').append(i + 1).append(": " + _("Gateway") + ' '); buf.append(_context.commSystem().renderPeerHTML(ls.getLease(i).getGateway())); buf.append(' ' + _("Tunnel") + ' ').append(ls.getLease(i).getTunnelId().getTunnelId()).append("<br>\n"); } buf.append("<hr>\n"); out.write(buf.toString()); buf.setLength(0); } out.write(buf.toString()); out.flush(); } /** * @param mode 0: our info and charts only; 1: full routerinfos and charts; 2: abbreviated routerinfos and charts */ public void renderStatusHTML(Writer out, int mode) throws IOException { out.write("<h2>" + _("Network Database Contents") + " (<a href=\"netdb.jsp?l=1\">" + _("View LeaseSets") + "</a>)</h2>\n"); if (!_context.netDb().isInitialized()) { out.write(_("Not initialized")); out.flush(); return; } boolean full = mode == 1; boolean shortStats = mode == 2; boolean showStats = full || shortStats; Hash us = _context.routerHash(); out.write("<a name=\"routers\" ></a><h3>" + _("Routers") + " (<a href=\"netdb.jsp"); if (full || !showStats) out.write("?f=2#routers\" >" + _("Show all routers")); else out.write("?f=1#routers\" >" + _("Show all routers with full stats")); out.write("</a>)</h3>\n"); StringBuilder buf = new StringBuilder(8192); RouterInfo ourInfo = _context.router().getRouterInfo(); renderRouterInfo(buf, ourInfo, true, true); out.write(buf.toString()); buf.setLength(0); ObjectCounter<String> versions = new ObjectCounter(); ObjectCounter<String> countries = new ObjectCounter(); Set routers = new TreeSet(new RouterInfoComparator()); routers.addAll(_context.netDb().getRouters()); for (Iterator iter = routers.iterator(); iter.hasNext(); ) { RouterInfo ri = (RouterInfo)iter.next(); Hash key = ri.getIdentity().getHash(); boolean isUs = key.equals(us); if (!isUs) { if (showStats) { renderRouterInfo(buf, ri, false, full); out.write(buf.toString()); buf.setLength(0); } String routerVersion = ri.getOption("router.version"); if (routerVersion != null) versions.increment(routerVersion); String country = _context.commSystem().getCountry(key); if(country != null) countries.increment(country); } } buf.append("<table border=\"0\" cellspacing=\"30\"><tr><td>"); List<String> versionList = new ArrayList(versions.objects()); if (versionList.size() > 0) { Collections.sort(versionList, Collections.reverseOrder(new TrustedUpdate.VersionComparator())); buf.append("<table>\n"); buf.append("<tr><th>" + _("Version") + "</th><th>" + _("Count") + "</th></tr>\n"); for (String routerVersion : versionList) { int num = versions.count(routerVersion); buf.append("<tr><td align=\"center\">").append(DataHelper.stripHTML(routerVersion)); buf.append("</td><td align=\"center\">").append(num).append("</td></tr>\n"); } buf.append("</table>\n"); } buf.append("</td><td>"); out.write(buf.toString()); buf.setLength(0); List<String> countryList = new ArrayList(countries.objects()); if (countryList.size() > 0) { Collections.sort(countryList, new CountryComparator()); buf.append("<table>\n"); buf.append("<tr><th align=\"left\">" + _("Country") + "</th><th>" + _("Count") + "</th></tr>\n"); for (String country : countryList) { int num = countries.count(country); buf.append("<tr><td><img height=\"11\" width=\"16\" alt=\"").append(country.toUpperCase()).append("\""); buf.append(" src=\"/flags.jsp?c=").append(country).append("\"> "); buf.append(_(_context.commSystem().getCountryName(country))); buf.append("</td><td align=\"center\">").append(num).append("</td></tr>\n"); } buf.append("</table>\n"); } buf.append("</td></tr></table>"); out.write(buf.toString()); out.flush(); } /** sort by translated country name */ private class CountryComparator implements Comparator { public int compare(Object l, Object r) { return _(_context.commSystem().getCountryName((String)l)) .compareTo(_(_context.commSystem().getCountryName((String)r))); } } /** * Be careful to use stripHTML for any displayed routerInfo data * to prevent vulnerabilities */ private void renderRouterInfo(StringBuilder buf, RouterInfo info, boolean isUs, boolean full) { String hash = info.getIdentity().getHash().toBase64(); buf.append("<table><tr><th><a name=\"").append(hash.substring(0, 6)).append("\" ></a>"); if (isUs) { buf.append("<a name=\"our-info\" ></a><b>" + _("Our info") + ": ").append(hash).append("</b></th></tr><tr><td>\n"); } else { buf.append("<b>" + _("Peer info for") + ":</b> ").append(hash).append("\n"); if (full) { buf.append("[<a href=\"netdb.jsp\" >Back</a>]</th></tr><td>\n"); } else { buf.append("[<a href=\"netdb.jsp?r=").append(hash.substring(0, 6)).append("\" >").append(_("Full entry")).append("</a>]</th></tr><td>\n"); } } long age = _context.clock().now() - info.getPublished(); if (isUs && _context.router().isHidden()) buf.append("<b>" + _("Hidden") + ", " + _("Updated") + ":</b> ").append(DataHelper.formatDuration(age)).append(" " + _("ago") + "<br>\n"); else if (age > 0) buf.append("<b>" + _("Published") + ":</b> ").append(DataHelper.formatDuration(age)).append(" " + _("ago") + "<br>\n"); else buf.append("<b>" + _("Published") + ":</b> in ").append(DataHelper.formatDuration(0-age)).append("???<br>\n"); buf.append("<b>" + _("Address(es)") + ":</b> "); String country = _context.commSystem().getCountry(info.getIdentity().getHash()); if(country != null) { buf.append("<img height=\"11\" width=\"16\" alt=\"").append(country.toUpperCase()).append("\""); buf.append(" src=\"/flags.jsp?c=").append(country).append("\"> "); } for (Iterator iter = info.getAddresses().iterator(); iter.hasNext(); ) { RouterAddress addr = (RouterAddress)iter.next(); buf.append("<b>").append(DataHelper.stripHTML(addr.getTransportStyle())).append("</b>: "); for (Iterator optIter = addr.getOptions().keySet().iterator(); optIter.hasNext(); ) { String name = (String)optIter.next(); String val = addr.getOptions().getProperty(name); buf.append('[').append(DataHelper.stripHTML(name)).append('=').append(DataHelper.stripHTML(val)).append("] "); } } buf.append("</td></tr>\n"); if (full) { buf.append("<tr><td>" + _("Stats") + ": <br><code>\n"); for (Iterator iter = info.getOptions().keySet().iterator(); iter.hasNext(); ) { String key = (String)iter.next(); String val = info.getOption(key); buf.append(DataHelper.stripHTML(key)).append(" = ").append(DataHelper.stripHTML(val)).append("<br>\n"); } buf.append("</code></td></tr>\n"); } else { } buf.append("</td></tr>\n"); } /** translate a string */ private String _(String s) { return Messages.getString(s, _context); } /** * translate a string with a parameter * This is a lot more expensive than _(s), so use sparingly. * * @param s string to be translated containing {0} * The {0} will be replaced by the parameter. * Single quotes must be doubled, i.e. ' -> '' in the string. * @param o parameter, not translated. * To tranlslate parameter also, use _("foo {0} bar", _("baz")) * Do not double the single quotes in the parameter. * Use autoboxing to call with ints, longs, floats, etc. */ private String _(String s, Object o) { return Messages.getString(s, o, _context); } }