[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] [metrics-web/master] Make exonerator.html use descriptors from the database.
Author: Karsten Loesing <karsten.loesing@xxxxxxx>
Date: Fri, 12 Nov 2010 16:31:22 +0100
Subject: Make exonerator.html use descriptors from the database.
Commit: 5a61949bba77cbe92097a059a3d8ee2c06b18e4a
Also fix parsing of accept/reject lines with port ranges.
---
.../torproject/ernie/web/ExoneraTorServlet.java | 525 ++++++++++----------
1 files changed, 260 insertions(+), 265 deletions(-)
diff --git a/src/org/torproject/ernie/web/ExoneraTorServlet.java b/src/org/torproject/ernie/web/ExoneraTorServlet.java
index 6f68f2c..e04619f 100644
--- a/src/org/torproject/ernie/web/ExoneraTorServlet.java
+++ b/src/org/torproject/ernie/web/ExoneraTorServlet.java
@@ -1,17 +1,41 @@
package org.torproject.ernie.web;
-import javax.servlet.*;
-import javax.servlet.http.*;
import java.io.*;
import java.math.*;
+import java.sql.*;
import java.text.*;
import java.util.*;
+import java.util.logging.*;
import java.util.regex.*;
+import javax.naming.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import javax.sql.*;
+
import org.apache.commons.codec.binary.*;
public class ExoneraTorServlet extends HttpServlet {
+ private DataSource ds;
+
+ private Logger logger;
+
+ public void init() {
+
+ /* Initialize logger. */
+ this.logger = Logger.getLogger(ExoneraTorServlet.class.toString());
+
+ /* Look up data source. */
+ try {
+ Context cxt = new InitialContext();
+ this.ds = (DataSource) cxt.lookup("java:comp/env/jdbc/tordir");
+ this.logger.info("Successfully looked up data source.");
+ } catch (NamingException e) {
+ this.logger.log(Level.WARNING, "Could not look up data source", e);
+ }
+ }
+
private void writeHeader(PrintWriter out) throws IOException {
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 "
+ "Transitional//EN\">\n"
@@ -35,18 +59,18 @@ public class ExoneraTorServlet extends HttpServlet {
+ "alt=\"Click to go to home page\" width=\"193\" "
+ "height=\"79\"></a></td>\n"
+ " <td class=\"banner-middle\">\n"
- + " <a href=\"/\">Home</a>\n"
- + " <a href=\"graphs.html\">Graphs</a>\n"
- + " <a href=\"research.html\">Research</a>\n"
- + " <a href=\"status.html\">Status</a>\n"
- + " <br>\n"
- + " <font size=\"2\">\n"
- + " <a class=\"current\">ExoneraTor</a>\n"
- + " <a href=\"relay-search.html\">Relay Search</a>\n"
-
- + " <a href=\"consensus-health.html\">Consensus Health</a>\n"
- + " </font>\n"
- + " </td>\n"
+ + " <a href=\"/\">Home</a>\n"
+ + " <a href=\"graphs.html\">Graphs</a>\n"
+ + " <a href=\"research.html\">Research</a>\n"
+ + " <a href=\"status.html\">Status</a>\n"
+ + " <br>\n"
+ + " <font size=\"2\">\n"
+ + " <a class=\"current\">ExoneraTor</a>\n"
+ + " <a href=\"relay-search.html\">Relay Search</a>\n"
+ + " <a href=\"consensus-health.html\">Consensus "
+ + "Health</a>\n"
+ + " </font>\n"
+ + " </td>\n"
+ " <td class=\"banner-right\"></td>\n"
+ " </tr>\n"
+ " </table>\n"
@@ -69,8 +93,8 @@ public class ExoneraTorServlet extends HttpServlet {
+ "server or who has access to this web server. If you need to "
+ "keep the IP addresses and incident times confidential, you "
+ "should download the <a href=\"tools.html#exonerator\">Java "
- + "or Python version of ExoneraTor</a> and run it on your "
- + "local machine.</font></p>\n"
+ + "or Python version of ExoneraTor</a> and run it on your "
+ + "local machine.</font></p>\n"
+ " <br>\n");
}
@@ -86,8 +110,8 @@ public class ExoneraTorServlet extends HttpServlet {
+ "do not necessarily reflect the views of the National "
+ "Science Foundation.</p>\n"
+ " <p>\"Tor\" and the \"Onion Logo\" are <a "
- + "href=\"https://www.torproject.org/docs/trademark-faq.html.en\">"
- + "registered trademarks</a> of The Tor Project, Inc.</p>\n"
+ + "href=\"https://www.torproject.org/docs/trademark-faq.html.en"
+ + "\">registered trademarks</a> of The Tor Project, Inc.</p>\n"
+ " <p>Data on this site is freely available under a <a "
+ "href=\"http://creativecommons.org/publicdomain/zero/1.0/\">"
+ "CC0 no copyright declaration</a>: To the extent possible "
@@ -103,38 +127,33 @@ public class ExoneraTorServlet extends HttpServlet {
out.close();
}
- // TODO make this configurable!
- public final String CONSENSUS_DIRECTORY =
- "/srv/metrics.torproject.org/ernie/directory-archive/consensus";
- public final String SERVER_DESCRIPTOR_DIRECTORY =
- "/srv/metrics.torproject.org/ernie/directory-archive/"
- + "server-descriptor";
-
- private static final boolean TEST_MODE = false; // TODO take me out
-
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws IOException,
ServletException {
- /* Get print writer and start writing response. We're wrapping the
- * PrintWriter, because we want it to auto-flush as soon as we have
- * written a line. */
- //PrintWriter out = new PrintWriter(response.getWriter(), true);
+ /* Start writing response. */
PrintWriter out = response.getWriter();
writeHeader(out);
- /* Check if we have a descriptors directory. */
- File consensusDirectory = new File(CONSENSUS_DIRECTORY);
- SortedSet<File> consensusDirectories = new TreeSet<File>();
- if (consensusDirectory.exists() && consensusDirectory.isDirectory()) {
- for (File yearFile : consensusDirectory.listFiles()) {
- for (File monthFile : yearFile.listFiles()) {
- consensusDirectories.add(monthFile);
- }
+ /* Look up first and last consensus in the database. */
+ long firstValidAfter = -1L, lastValidAfter = -1L;
+ try {
+ Connection conn = this.ds.getConnection();
+ Statement statement = conn.createStatement();
+ String query = "SELECT MIN(validafter) AS first, "
+ + "MAX(validafter) AS last FROM consensus";
+ ResultSet rs = statement.executeQuery(query);
+ if (rs.next()) {
+ firstValidAfter = rs.getTimestamp(1).getTime();
+ lastValidAfter = rs.getTimestamp(2).getTime();
}
+ rs.close();
+ statement.close();
+ conn.close();
+ } catch (SQLException e) {
+ /* Looks like we don't have any consensuses. */
}
-
- if (consensusDirectories.isEmpty()) {
+ if (firstValidAfter < 0L || lastValidAfter < 0L) {
out.println("<p><font color=\"red\"><b>Warning: </b></font>This "
+ "server doesn't have any relay lists available. If this "
+ "problem persists, please "
@@ -143,21 +162,9 @@ public class ExoneraTorServlet extends HttpServlet {
writeFooter(out);
return;
}
- String firstConsensus = new TreeSet<File>(Arrays.asList(
- new TreeSet<File>(Arrays.asList(consensusDirectories.first().
- listFiles())).first().listFiles())).first().getName().substring(0,
- 13);
- firstConsensus = firstConsensus.substring(0, 10) + " "
- + firstConsensus.substring(11, 13) + ":00";
- String lastConsensus = new TreeSet<File>(Arrays.asList(
- new TreeSet<File>(Arrays.asList(consensusDirectories.last().
- listFiles())).last().listFiles())).last().getName().substring(0,
- 13);
- lastConsensus = lastConsensus.substring(0, 10) + " "
- + lastConsensus.substring(11, 13) + ":00";
-
- out.println("<a name=\"relay\"></a><h3>Was there a Tor relay running on "
- + "this IP address?</h3>");
+
+ out.println("<a name=\"relay\"></a><h3>Was there a Tor relay running "
+ + "on this IP address?</h3>");
/* Parse IP parameter. */
Pattern ipAddressPattern = Pattern.compile(
@@ -186,19 +193,18 @@ public class ExoneraTorServlet extends HttpServlet {
String timestampParameter = request.getParameter("timestamp");
long timestamp = 0L;
String timestampStr = "", timestampWarning = "";
- SimpleDateFormat parseTimeFormat = new SimpleDateFormat(
+ SimpleDateFormat shortDateTimeFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm");
- parseTimeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ shortDateTimeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
if (timestampParameter != null && timestampParameter.length() > 0) {
try {
- Date parsedTimestamp = parseTimeFormat.parse(timestampParameter);
- if (timestampParameter.compareTo(firstConsensus) >= 0 &&
- timestampParameter.compareTo(lastConsensus) <= 0) {
- timestamp = parsedTimestamp.getTime();
- timestampStr = parseTimeFormat.format(timestamp);
- } else {
+ timestamp = shortDateTimeFormat.parse(timestampParameter).
+ getTime();
+ timestampStr = shortDateTimeFormat.format(timestamp);
+ if (timestamp < firstValidAfter || timestamp > lastValidAfter) {
timestampWarning = "Please pick a value between \""
- + firstConsensus + "\" and \"" + lastConsensus + "\".";
+ + shortDateTimeFormat.format(firstValidAfter) + "\" and \""
+ + shortDateTimeFormat.format(lastValidAfter) + "\".";
}
} catch (ParseException e) {
/* We have no way to handle this exception, other than leaving
@@ -282,10 +288,11 @@ public class ExoneraTorServlet extends HttpServlet {
+ ">\n"
+ " <table>\n"
+ " <tr>\n"
- + " <td align=\"right\">IP address in question:</td>\n"
+ + " <td align=\"right\">IP address in question:"
+ + "</td>\n"
+ " <td><input type=\"text\" name=\"ip\""
- + (relayIP.length() > 0 ? " value=\"" + relayIP + "\"" :
- (TEST_MODE ? " value=\"209.17.171.104\"" : ""))
+ + (relayIP.length() > 0 ? " value=\"" + relayIP + "\""
+ : "")
+ ">"
+ (ipWarning.length() > 0 ? "<br><font color=\"red\">"
+ ipWarning + "</font>" : "")
@@ -295,8 +302,8 @@ public class ExoneraTorServlet extends HttpServlet {
+ " <tr>\n"
+ " <td align=\"right\">Timestamp, in UTC:</td>\n"
+ " <td><input type=\"text\" name=\"timestamp\""
- + (timestampStr.length() > 0 ? " value=\"" + timestampStr + "\"" :
- (TEST_MODE ? " value=\"2009-08-15 16:05\"" : ""))
+ + (timestampStr.length() > 0 ? " value=\"" + timestampStr + "\""
+ : "")
+ ">"
+ (timestampWarning.length() > 0 ? "<br><font color=\"red\">"
+ timestampWarning + "</font>" : "")
@@ -323,79 +330,54 @@ public class ExoneraTorServlet extends HttpServlet {
long timestampTooOld = timestamp - 15L * 60L * 60L * 1000L;
long timestampFrom = timestamp - 3L * 60L * 60L * 1000L;
long timestampTooNew = timestamp + 12L * 60L * 60L * 1000L;
- Calendar calTooOld = Calendar.getInstance(
- TimeZone.getTimeZone("UTC"));
- Calendar calFrom = Calendar.getInstance(
- TimeZone.getTimeZone("UTC"));
- Calendar calTooNew = Calendar.getInstance(
- TimeZone.getTimeZone("UTC"));
- calTooOld.setTimeInMillis(timestampTooOld);
- calFrom.setTimeInMillis(timestampFrom);
- calTooNew.setTimeInMillis(timestampTooNew);
out.printf("<p>Looking up IP address %s in the relay lists published "
- + "between %tF %tR and %s. "
+ + "between %s and %s. "
+ "Clients could have used any of these relay lists to "
+ "select relays for their paths and build circuits using them. "
+ "You may follow the links to relay lists and relay descriptors "
+ "to grep for the lines printed below and confirm that results "
- + "are correct.<br>", relayIP, calFrom, calFrom, timestampStr);
- SimpleDateFormat consensusTimeFormat = new SimpleDateFormat(
- "yyyy-MM-dd-HH-mm-ss");
- consensusTimeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- String fromTime = consensusTimeFormat.format(
- new Date(timestampTooOld));
- String fromDay = fromTime.substring(0, "yyyy-MM-dd".length());
- String fromMonth = fromDay.substring(0, "yyyy-MM".length());
- String toTime = consensusTimeFormat.format(new Date(timestampTooNew));
- String toDay = toTime.substring(0, "yyyy-MM-dd".length());
- String toMonth = toDay.substring(0, "yyyy-MM".length());
- SortedSet<File> tooOldConsensuses = new TreeSet<File>();
- SortedSet<File> relevantConsensuses = new TreeSet<File>();
- SortedSet<File> tooNewConsensuses = new TreeSet<File>();
- for (File consensusMonth : consensusDirectories) {
- String month = consensusMonth.getParentFile().getName() + "-"
- + consensusMonth.getName();
- if (month.compareTo(fromMonth) < 0 ||
- month.compareTo(toMonth) > 0) {
- continue;
- }
- for (File consensusDay : consensusMonth.listFiles()) {
- String day = month + "-" + consensusDay.getName();
- if (day.compareTo(fromDay) < 0 ||
- day.compareTo(toDay) > 0) {
- continue;
- }
- for (File consensusFile : consensusDay.listFiles()) {
- String time = consensusFile.getName().substring(0,
- "yyyy-MM-dd-HH-mm-ss".length());
- if (time.compareTo(fromTime) < 0 ||
- time.compareTo(toTime) > 0) {
- continue;
- }
- Date consensusDate = null;
- try {
- consensusDate = consensusTimeFormat.parse(time);
- } catch (ParseException e) {
- /* This should never happen. If it does, it's a bug. */
- throw new RuntimeException(e);
- }
- long consensusTime = consensusDate.getTime();
- if (consensusTime >= timestampTooOld &&
- consensusTime < timestampFrom)
- tooOldConsensuses.add(consensusFile);
- else if (consensusTime >= timestampFrom &&
- consensusTime <= timestamp)
- relevantConsensuses.add(consensusFile);
- else if (consensusTime > timestamp &&
- consensusTime <= timestampTooNew)
- tooNewConsensuses.add(consensusFile);
+ + "are correct.<br>", relayIP,
+ shortDateTimeFormat.format(timestampFrom), timestampStr);
+ SimpleDateFormat validAfterTimeFormat = new SimpleDateFormat(
+ "yyyy-MM-dd HH:mm:ss");
+ validAfterTimeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ String fromValidAfter = validAfterTimeFormat.format(timestampTooOld);
+ String toValidAfter = validAfterTimeFormat.format(timestampTooNew);
+ SortedMap<Long, String> tooOldConsensuses =
+ new TreeMap<Long, String>();
+ SortedMap<Long, String> relevantConsensuses =
+ new TreeMap<Long, String>();
+ SortedMap<Long, String> tooNewConsensuses =
+ new TreeMap<Long, String>();
+ try {
+ Connection conn = this.ds.getConnection();
+ Statement statement = conn.createStatement();
+ String query = "SELECT validafter, rawdesc FROM consensus "
+ + "WHERE validafter >= '" + fromValidAfter
+ + "' AND validafter <= '" + toValidAfter + "'";
+ ResultSet rs = statement.executeQuery(query);
+ while (rs.next()) {
+ long consensusTime = rs.getTimestamp(1).getTime();
+ String rawConsensusString = new String(rs.getBytes(2), "US-ASCII");
+ if (consensusTime < timestampFrom) {
+ tooOldConsensuses.put(consensusTime, rawConsensusString);
+ } else if (consensusTime > timestamp) {
+ tooNewConsensuses.put(consensusTime, rawConsensusString);
+ } else {
+ relevantConsensuses.put(consensusTime, rawConsensusString);
}
}
+ rs.close();
+ statement.close();
+ conn.close();
+ } catch (SQLException e) {
+ /* Looks like we don't have any consensuses in the requested
+ interval. */
}
- SortedSet<File> allConsensuses = new TreeSet<File>();
- allConsensuses.addAll(tooOldConsensuses);
- allConsensuses.addAll(relevantConsensuses);
- allConsensuses.addAll(tooNewConsensuses);
+ SortedMap<Long, String> allConsensuses = new TreeMap<Long, String>();
+ allConsensuses.putAll(tooOldConsensuses);
+ allConsensuses.putAll(relevantConsensuses);
+ allConsensuses.putAll(tooNewConsensuses);
if (allConsensuses.isEmpty()) {
out.println(" <p>No relay lists found!</p>\n"
+ " <p>Result is INDECISIVE!</p>\n"
@@ -410,48 +392,46 @@ public class ExoneraTorServlet extends HttpServlet {
return;
}
- // parse consensuses to find descriptors belonging to the IP address
- SortedSet<File> positiveConsensusesNoTarget = new TreeSet<File>();
+ /* Parse consensuses to find descriptors belonging to the IP
+ address. */
+ SortedSet<Long> positiveConsensusesNoTarget = new TreeSet<Long>();
Set<String> addressesInSameNetwork = new HashSet<String>();
- SortedMap<String, Set<File>> relevantDescriptors =
- new TreeMap<String, Set<File>>();
- SimpleDateFormat validAfterTimeFormat = new SimpleDateFormat(
- "yyyy-MM-dd HH:mm:ss");
- validAfterTimeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- for (File consensus : allConsensuses) {
- if (relevantConsensuses.contains(consensus)) {
- String validAfterString = consensus.getName().substring(0,
- "yyyy-MM-dd-HH-mm-ss".length());
- Date validAfterDate = null;
- try {
- validAfterDate = consensusTimeFormat.parse(validAfterString);
- } catch (ParseException e) {
- /* This should never happen. If it does, it's a bug. */
- throw new RuntimeException(e);
- }
- long validAfterTime = validAfterDate.getTime();
+ SortedMap<String, Set<Long>> relevantDescriptors =
+ new TreeMap<String, Set<Long>>();
+ SimpleDateFormat validAfterUrlFormat = new SimpleDateFormat(
+ "yyyy-MM-dd-HH-mm-ss");
+ validAfterUrlFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ for (Map.Entry<Long, String> e : allConsensuses.entrySet()) {
+ long consensus = e.getKey();
+ if (relevantConsensuses.containsKey(consensus)) {
+ long validAfterTime = -1L;
String validAfterDatetime = validAfterTimeFormat.format(
- validAfterTime);
+ consensus);
+ String validAfterString = validAfterUrlFormat.format(consensus);
out.println(" <br><tt>valid-after <b>"
+ "<a href=\"consensus?valid-after="
+ validAfterString + "\" target=\"_blank\">"
+ validAfterDatetime + "</b></a></tt><br>");
}
- BufferedReader br = new BufferedReader(new FileReader(consensus));
- String line;
+ String rawConsensusString = e.getValue();
+ BufferedReader br = new BufferedReader(new StringReader(
+ rawConsensusString));
+ String line = null;
while ((line = br.readLine()) != null) {
- if (!line.startsWith("r "))
+ if (!line.startsWith("r ")) {
continue;
+ }
String[] parts = line.split(" ");
String address = parts[6];
if (address.equals(relayIP)) {
String hex = String.format("%040x", new BigInteger(1,
Base64.decodeBase64(parts[3] + "==")));
- if (!relevantDescriptors.containsKey(hex))
- relevantDescriptors.put(hex, new HashSet<File>());
+ if (!relevantDescriptors.containsKey(hex)) {
+ relevantDescriptors.put(hex, new HashSet<Long>());
+ }
relevantDescriptors.get(hex).add(consensus);
positiveConsensusesNoTarget.add(consensus);
- if (relevantConsensuses.contains(consensus)) {
+ if (relevantConsensuses.containsKey(consensus)) {
out.println(" <tt>r " + parts[1] + " " + parts[2] + " "
+ "<a href=\"serverdesc?desc-id=" + hex + "\" "
+ "target=\"_blank\">" + parts[3] + "</a> " + parts[4]
@@ -472,12 +452,13 @@ public class ExoneraTorServlet extends HttpServlet {
+ " <p>Result is NEGATIVE with moderate certainty!</p>\n"
+ " <p>We did not find IP "
+ "address " + relayIP + " in any of the relay lists that were "
- + "published between %tF %tR and %tF %tR.\n\nA possible "
+ + "published between %s and %s.\n\nA possible "
+ "reason for false negatives is that the relay is using a "
+ "different IP address when generating a descriptor than for "
+ "exiting to the Internet. We hope to provide better checks "
- + "for this case in the future.</p>\n", calTooOld, calTooOld,
- calTooNew, calTooNew);
+ + "for this case in the future.</p>\n",
+ shortDateTimeFormat.format(timestampTooOld),
+ shortDateTimeFormat.format(timestampTooNew));
if (!addressesInSameNetwork.isEmpty()) {
out.println(" <p>The following other IP addresses of Tor "
+ "relays were found in the mentioned relay lists that "
@@ -491,9 +472,9 @@ public class ExoneraTorServlet extends HttpServlet {
return;
}
- // print out result
- Set<File> matches = positiveConsensusesNoTarget;
- if (matches.contains(relevantConsensuses.last())) {
+ /* Print out result. */
+ Set<Long> matches = positiveConsensusesNoTarget;
+ if (matches.contains(relevantConsensuses.lastKey())) {
out.println(" <p>Result is POSITIVE with high certainty!"
+ "</p>\n"
+ " <p>We found one or more relays on IP address "
@@ -504,24 +485,25 @@ public class ExoneraTorServlet extends HttpServlet {
boolean inOtherRelevantConsensus = false,
inTooOldConsensuses = false,
inTooNewConsensuses = false;
- for (File f : matches)
- if (relevantConsensuses.contains(f))
+ for (long match : matches) {
+ if (relevantConsensuses.containsKey(match)) {
inOtherRelevantConsensus = true;
- else if (tooOldConsensuses.contains(f))
+ } else if (tooOldConsensuses.containsKey(match)) {
inTooOldConsensuses = true;
- else if (tooNewConsensuses.contains(f))
+ } else if (tooNewConsensuses.containsKey(match)) {
inTooNewConsensuses = true;
+ }
+ }
if (inOtherRelevantConsensus) {
out.println(" <p>Result is POSITIVE "
+ "with moderate certainty!</p>\n");
out.println("<p>We found one or more relays on IP address "
- + relayIP
- + ", but not in the relay list immediately preceding "
- + timestampStr + ". A possible reason for the relay being "
- + "missing in the last relay list preceding the given time might "
- + "be that some of the directory authorities had difficulties "
- + "connecting to the relay. However, clients might still have "
- + "used the relay.</p>\n");
+ + relayIP + ", but not in the relay list immediately "
+ + "preceding " + timestampStr + ". A possible reason for the "
+ + "relay being missing in the last relay list preceding the "
+ + "given time might be that some of the directory "
+ + "authorities had difficulties connecting to the relay. "
+ + "However, clients might still have used the relay.</p>\n");
} else {
out.println(" <p>Result is NEGATIVE "
+ "with high certainty!</p>\n");
@@ -531,18 +513,18 @@ public class ExoneraTorServlet extends HttpServlet {
+ ".</p>\n");
if (inTooOldConsensuses || inTooNewConsensuses) {
if (inTooOldConsensuses && !inTooNewConsensuses) {
- out.println(" <p>Note that we found a matching relay in "
- + "relay lists that were published between 5 and 3 "
+ out.println(" <p>Note that we found a matching relay "
+ + "in relay lists that were published between 5 and 3 "
+ "hours before " + timestampStr + ".</p>\n");
} else if (!inTooOldConsensuses && inTooNewConsensuses) {
- out.println(" <p>Note that we found a matching relay in "
- + "relay lists that were published up to 2 hours after "
- + timestampStr + ".</p>\n");
+ out.println(" <p>Note that we found a matching relay "
+ + "in relay lists that were published up to 2 hours "
+ + "after " + timestampStr + ".</p>\n");
} else {
- out.println(" <p>Note that we found a matching relay in "
- + "relay lists that were published between 5 and 3 "
- + "hours before and in relay lists that were published up "
- + "to 2 hours after " + timestampStr + ".</p>\n");
+ out.println(" <p>Note that we found a matching relay "
+ + "in relay lists that were published between 5 and 3 "
+ + "hours before and in relay lists that were published "
+ + "up to 2 hours after " + timestampStr + ".</p>\n");
}
out.println("<p>Make sure that the timestamp you provided is "
+ "in the correct timezone: UTC (or GMT).</p>");
@@ -553,30 +535,8 @@ public class ExoneraTorServlet extends HttpServlet {
}
/* Second part: target */
- out.println("<br><a name=\"exit\"></a><h3>Was this relay configured to "
- + "permit exiting to a given target?</h3>");
-
- File serverDescriptorDirectory =
- new File(SERVER_DESCRIPTOR_DIRECTORY);
- SortedSet<File> serverDescriptorDirectories = new TreeSet<File>();
- if (serverDescriptorDirectory.exists() &&
- serverDescriptorDirectory.isDirectory()) {
- for (File yearFile : serverDescriptorDirectory.listFiles()) {
- for (File monthFile : yearFile.listFiles()) {
- serverDescriptorDirectories.add(monthFile);
- }
- }
- }
-
- if (serverDescriptorDirectories.isEmpty()) {
- out.println("<p><font color=\"red\"><b>Warning: </b></font>This "
- + "server doesn't have any relay descriptors available. If "
- + "this problem persists, please "
- + "<a href=\"mailto:tor-assistants@xxxxxxxxxxxxx\">let us "
- + "know</a>!</p>\n");
- writeFooter(out);
- return;
- }
+ out.println("<br><a name=\"exit\"></a><h3>Was this relay configured "
+ + "to permit exiting to a given target?</h3>");
out.println(" <form action=\"exonerator.html#exit\">\n"
+ " <input type=\"hidden\" name=\"timestamp\"\n"
@@ -587,8 +547,7 @@ public class ExoneraTorServlet extends HttpServlet {
+ " <tr>\n"
+ " <td align=\"right\">Target address:</td>\n"
+ " <td><input type=\"text\" name=\"targetaddr\""
- + (targetIP.length() > 0 ? " value=\"" + targetIP + "\"" :
- (TEST_MODE ? " value=\"209.85.129.104\"" : ""))
+ + (targetIP.length() > 0 ? " value=\"" + targetIP + "\"" : "")
+ "\">"
+ (targetAddrWarning.length() > 0 ? "<br><font color=\"red\">"
+ targetAddrWarning + "</font>" : "")
@@ -598,8 +557,8 @@ public class ExoneraTorServlet extends HttpServlet {
+ " <tr>\n"
+ " <td align=\"right\">Target port:</td>\n"
+ " <td><input type=\"text\" name=\"targetport\""
- + (targetPort.length() > 0 ? " value=\"" + targetPort + "\"" :
- (TEST_MODE ? " value=\"80\"" : ""))
+ + (targetPort.length() > 0 ? " value=\"" + targetPort + "\""
+ : "")
+ ">"
+ (targetPortWarning.length() > 0 ? "<br><font color=\"red\">"
+ targetPortWarning + "</font>" : "")
@@ -622,30 +581,40 @@ public class ExoneraTorServlet extends HttpServlet {
return;
}
- // parse router descriptors to check exit policies
- out.println("<p>Searching the relay descriptors published by the relay "
- + "on IP address " + relayIP + " to find out whether this relay "
- + "permitted exiting to " + target + ". You may follow the links "
- + "above to the relay descriptors and grep them for the lines "
- + "printed below to confirm that results are correct.</p>");
- SortedSet<File> positiveConsensuses = new TreeSet<File>();
+ /* Parse router descriptors to check exit policies. */
+ out.println("<p>Searching the relay descriptors published by the "
+ + "relay on IP address " + relayIP + " to find out whether this "
+ + "relay permitted exiting to " + target + ". You may follow the "
+ + "links above to the relay descriptors and grep them for the "
+ + "lines printed below to confirm that results are correct.</p>");
+ SortedSet<Long> positiveConsensuses = new TreeSet<Long>();
Set<String> missingDescriptors = new HashSet<String>();
Set<String> descriptors = relevantDescriptors.keySet();
for (String descriptor : descriptors) {
- for (File directory : serverDescriptorDirectories) {
- File subDirectory = new File(directory.getAbsolutePath() + "/"
- + descriptor.substring(0, 1) + "/"
- + descriptor.substring(1, 2));
- if (subDirectory.exists()) {
- File descriptorFile = new File(subDirectory.getAbsolutePath()
- + "/" + descriptor);
- if (!descriptorFile.exists()) {
- continue;
- }
- missingDescriptors.remove(descriptor);
+ byte[] rawDescriptor = null;
+ try {
+ Connection conn = this.ds.getConnection();
+ Statement statement = conn.createStatement();
+ String query = "SELECT rawdesc FROM descriptor "
+ + "WHERE descriptor = '" + descriptor + "'";
+ ResultSet rs = statement.executeQuery(query);
+ if (rs.next()) {
+ rawDescriptor = rs.getBytes(1);
+ }
+ rs.close();
+ statement.close();
+ conn.close();
+ } catch (SQLException e) {
+ /* Consider this descriptors as 'missing'. */
+ continue;
+ }
+ if (rawDescriptor != null && rawDescriptor.length > 0) {
+ missingDescriptors.remove(descriptor);
+ String rawDescriptorString = new String(rawDescriptor, "US-ASCII");
+ try {
BufferedReader br = new BufferedReader(
- new FileReader(descriptorFile));
- String line, routerLine = null, publishedLine = null;
+ new StringReader(rawDescriptorString));
+ String line = null, routerLine = null, publishedLine = null;
StringBuilder acceptRejectLines = new StringBuilder();
boolean foundMatch = false;
while ((line = br.readLine()) != null) {
@@ -664,8 +633,9 @@ public class ExoneraTorServlet extends HttpServlet {
if (!ruleAddress.equals("*")) {
if (!ruleAddress.contains("/") &&
!ruleAddress.equals(targetIP)) {
+ /* IP address does not match. */
acceptRejectLines.append("<tt> " + line + "</tt><br>\n");
- continue; // IP address does not match
+ continue;
}
String[] ruleIPParts = ruleAddress.split("/")[0].
split("\\.");
@@ -690,28 +660,45 @@ public class ExoneraTorServlet extends HttpServlet {
}
}
if (ruleNetwork > 0) {
+ /* IP address does not match. */
acceptRejectLines.append("<tt> " + line + "</tt><br>\n");
- continue; // IP address does not match
+ continue;
}
}
String rulePort = line.split(" ")[1].split(":")[1];
if (targetPort.length() < 1 && !ruleAccept &&
!rulePort.equals("*")) {
+ /* With no port given, we only consider reject :* rules as
+ matching. */
acceptRejectLines.append("<tt> " + line + "</tt><br>\n");
- continue; // with no port given, we only consider
- // reject :* rules as matching
+ continue;
+ }
+ if (targetPort.length() > 0 && !rulePort.equals("*") &&
+ rulePort.contains("-")) {
+ int fromPort = Integer.parseInt(rulePort.split("-")[0]);
+ int toPort = Integer.parseInt(rulePort.split("-")[1]);
+ int targetPortInt = Integer.parseInt(targetPort);
+ if (targetPortInt < fromPort ||
+ targetPortInt > toPort) {
+ /* Port not contained in interval. */
+ continue;
+ }
}
if (targetPort.length() > 0) {
if (!rulePort.equals("*") &&
+ !rulePort.contains("-") &&
!targetPort.equals(rulePort)) {
+ /* Ports do not match. */
acceptRejectLines.append("<tt> " + line + "</tt><br>\n");
- continue; // ports do not match
+ continue;
}
}
boolean relevantMatch = false;
- for (File f : relevantDescriptors.get(descriptor))
- if (relevantConsensuses.contains(f))
+ for (long match : relevantDescriptors.get(descriptor)) {
+ if (relevantConsensuses.containsKey(match)) {
relevantMatch = true;
+ }
+ }
if (relevantMatch) {
String[] routerParts = routerLine.split(" ");
out.println("<br><tt>" + routerParts[0] + " "
@@ -733,15 +720,18 @@ public class ExoneraTorServlet extends HttpServlet {
}
}
br.close();
+ } catch (IOException e) {
+ /* Could not read descriptor string. */
+ continue;
}
}
}
- // print out result
-// TODO don't repeat results from above
+ /* Print out result. */
matches = positiveConsensuses;
- if (matches.contains(relevantConsensuses.last())) {
- out.println(" <p>Result is POSITIVE with high certainty!</p>\n"
+ if (matches.contains(relevantConsensuses.lastKey())) {
+ out.println(" <p>Result is POSITIVE with high certainty!</p>"
+ + "\n"
+ " <p>We found one or more relays on IP address "
+ relayIP + " permitting exit to " + target
+ " in the most recent relay list preceding " + timestampStr
@@ -753,35 +743,38 @@ public class ExoneraTorServlet extends HttpServlet {
&& !missingDescriptors.isEmpty();
if (resultIndecisive) {
out.println(" <p>Result is INDECISIVE!</p>\n"
- + " <p>At least one referenced descriptor could not be found. This "
- + "is a rare case, but one that (apparently) happens. We cannot "
- + "make any good statement about exit relays without these "
- + "descriptors. The following descriptors are missing:</p>");
+ + " <p>At least one referenced descriptor could not be "
+ + "found. This is a rare case, but one that (apparently) "
+ + "happens. We cannot make any good statement about exit "
+ + "relays without these descriptors. The following descriptors "
+ + "are missing:</p>");
for (String desc : missingDescriptors)
out.println(" <p>" + desc + "</p>\n");
}
boolean inOtherRelevantConsensus = false, inTooOldConsensuses = false,
inTooNewConsensuses = false;
- for (File f : matches)
- if (relevantConsensuses.contains(f))
+ for (long match : matches) {
+ if (relevantConsensuses.containsKey(match)) {
inOtherRelevantConsensus = true;
- else if (tooOldConsensuses.contains(f))
+ } else if (tooOldConsensuses.containsKey(match)) {
inTooOldConsensuses = true;
- else if (tooNewConsensuses.contains(f))
+ } else if (tooNewConsensuses.containsKey(match)) {
inTooNewConsensuses = true;
+ }
+ }
if (inOtherRelevantConsensus) {
if (!resultIndecisive) {
out.println(" <p>Result is POSITIVE "
+ "with moderate certainty!</p>\n");
}
out.println("<p>We found one or more relays on IP address "
- + relayIP + " permitting exit to " + target
- + ", but not in the relay list immediately preceding "
- + timestampStr + ". A possible reason for the relay being "
- + "missing in the last relay list preceding the given time might "
- + "be that some of the directory authorities had difficulties "
- + "connecting to the relay. However, clients might still have "
- + "used the relay.</p>\n");
+ + relayIP + " permitting exit to " + target + ", but not in "
+ + "the relay list immediately preceding " + timestampStr
+ + ". A possible reason for the relay being missing in the last "
+ + "relay list preceding the given time might be that some of "
+ + "the directory authorities had difficulties connecting to "
+ + "the relay. However, clients might still have used the "
+ + "relay.</p>\n");
} else {
if (!resultIndecisive) {
out.println(" <p>Result is NEGATIVE "
@@ -792,29 +785,31 @@ public class ExoneraTorServlet extends HttpServlet {
+ " in the relay list 3 hours preceding " + timestampStr
+ ".</p>\n");
if (inTooOldConsensuses || inTooNewConsensuses) {
- if (inTooOldConsensuses && !inTooNewConsensuses)
+ if (inTooOldConsensuses && !inTooNewConsensuses) {
out.println(" <p>Note that we found a matching relay in "
+ "relay lists that were published between 5 and 3 "
+ "hours before " + timestampStr + ".</p>\n");
- else if (!inTooOldConsensuses && inTooNewConsensuses)
+ } else if (!inTooOldConsensuses && inTooNewConsensuses) {
out.println(" <p>Note that we found a matching relay in "
+ "relay lists that were published up to 2 hours after "
+ timestampStr + ".</p>\n");
- else
+ } else {
out.println(" <p>Note that we found a matching relay in "
+ "relay lists that were published between 5 and 3 "
+ "hours before and in relay lists that were published up "
+ "to 2 hours after " + timestampStr + ".</p>\n");
+ }
out.println("<p>Make sure that the timestamp you provided is "
+ "in the correct timezone: UTC (or GMT).</p>");
}
}
if (target != null) {
if (positiveConsensuses.isEmpty() &&
- !positiveConsensusesNoTarget.isEmpty())
+ !positiveConsensusesNoTarget.isEmpty()) {
out.println(" <p>Note that although the found relay(s) did "
+ "not permit exiting to " + target + ", there have been one "
+ "or more relays running at the given time.</p>");
+ }
}
/* Finish writing response. */
--
1.7.1