commit 18d63dd7f414fb18940b254da8c3d6b5a7f54a95 Author: Karsten Loesing karsten.loesing@gmx.net Date: Wed Jun 11 17:22:11 2014 +0200
Disable relay search and remove it from navigation. --- website/etc/web.xml | 11 - .../org/torproject/metrics/web/StatusServlet.java | 24 - .../metrics/web/status/RelaySearchServlet.java | 514 +------------------- website/web/WEB-INF/banner.jsp | 10 - website/web/WEB-INF/error.jsp | 4 - website/web/WEB-INF/relay-search.jsp | 66 +-- 6 files changed, 4 insertions(+), 625 deletions(-)
diff --git a/website/etc/web.xml b/website/etc/web.xml index 519a9e6..a78cd28 100644 --- a/website/etc/web.xml +++ b/website/etc/web.xml @@ -138,17 +138,6 @@ </servlet-mapping>
<servlet> - <servlet-name>Status</servlet-name> - <servlet-class> - org.torproject.metrics.web.StatusServlet - </servlet-class> - </servlet> - <servlet-mapping> - <servlet-name>Status</servlet-name> - <url-pattern>/status.html</url-pattern> - </servlet-mapping> - - <servlet> <servlet-name>RelaySearch</servlet-name> <servlet-class> org.torproject.metrics.web.status.RelaySearchServlet diff --git a/website/src/org/torproject/metrics/web/StatusServlet.java b/website/src/org/torproject/metrics/web/StatusServlet.java deleted file mode 100644 index ec831df..0000000 --- a/website/src/org/torproject/metrics/web/StatusServlet.java +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright 2011, 2012 The Tor Project - * See LICENSE for licensing information */ -package org.torproject.metrics.web; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public class StatusServlet extends HttpServlet { - - private static final long serialVersionUID = -7249872082399236981L; - - public void doGet(HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { - - /* Forward the request to the JSP that does all the hard work. */ - request.getRequestDispatcher("WEB-INF/status.jsp").forward(request, - response); - } -} - diff --git a/website/src/org/torproject/metrics/web/status/RelaySearchServlet.java b/website/src/org/torproject/metrics/web/status/RelaySearchServlet.java index 49b091e..aac7f10 100644 --- a/website/src/org/torproject/metrics/web/status/RelaySearchServlet.java +++ b/website/src/org/torproject/metrics/web/status/RelaySearchServlet.java @@ -3,531 +3,21 @@ package org.torproject.metrics.web.status;
import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.SortedMap; -import java.util.SortedSet; -import java.util.TimeZone; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern;
-import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.sql.DataSource;
-import org.apache.commons.codec.binary.Base64; - -/** - * Web page that allows users to search for relays in the descriptor - * archives. - * - * Possible search terms for testing: - * - gabelmoo - * - gabelmoo 2010-09 - * - gabelmoo 2010-09-18 - * - gabelmoo $F2044413DAC2E02E3D6BCF4735A19BCA1DE97281 - * - gabelmoo 80.190.246 - * - gabelmoo $F2044413DAC2E02E3D6BCF4735A19BCA1DE97281 80.190.246 - * - 5898549205 dc737cc9dca16af6 79.212.74.45 - * - 5898549205 dc737cc9dca16af6 - * - 80.190.246.100 - * - $F2044413DAC2E02E3D6BCF4735A19BCA1DE97281 - * - $F2044413DAC2E02E3D6BCF4735A19BCA1DE97281 80.190.246 - * - 58985492 - * - 58985492 79.212.74.45 - */ public class RelaySearchServlet extends HttpServlet {
- private static final long serialVersionUID = -1772662230310611805L; - - private Pattern alphaNumDotDashDollarSpacePattern = - Pattern.compile("[A-Za-z0-9\.\-$ ]+"); - - private Pattern numPattern = Pattern.compile("[0-9]+"); - - private Pattern hexPattern = Pattern.compile("[A-Fa-f0-9]+"); - - private Pattern alphaNumPattern = Pattern.compile("[A-Za-z0-9]+"); - - private SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd"); - - private SimpleDateFormat monthFormat = new SimpleDateFormat("yyyy-MM"); - - private SimpleDateFormat dateTimeFormat = - new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - - private long minValidAfterMillis; - - private DataSource ds; - - private Logger logger; - - public void init() { - - /* Initialize logger. */ - this.logger = Logger.getLogger(RelaySearchServlet.class.toString()); - - /* Initialize date format parsers. */ - dayFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - monthFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - dateTimeFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - - /* 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); - } - - /* Look up first consensus in the database. */ - try { - long requestedConnection = System.currentTimeMillis(); - Connection conn = this.ds.getConnection(); - String query = "SELECT MIN(validafter) AS first FROM consensus"; - Statement statement = conn.createStatement(); - ResultSet rs = statement.executeQuery(query); - if (rs.next()) { - this.minValidAfterMillis = rs.getTimestamp(1).getTime(); - } - rs.close(); - statement.close(); - conn.close(); - this.logger.info("Returned a database connection to the pool " - + "after " + (System.currentTimeMillis() - - requestedConnection) + " millis."); - } catch (SQLException e) { - this.logger.log(Level.WARNING, "Could not look up first consensus " - + "valid-after time in the database.", e); - } - } + private static final long serialVersionUID = -1772662230310611806L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
- /* Read search parameter. If we don't have a search parameter, we're - * done here. */ - String searchParameter = request.getParameter("search"); - if (searchParameter == null || searchParameter.length() == 0) { - request.getRequestDispatcher("WEB-INF/relay-search.jsp").forward( - request, response); - return; - } - - /* Parse search parameter to identify what nickname, fingerprint, - * and/or IP address to search for. A valid query contains no more - * than one identifier for each of the fields. As a special case, - * there are search terms consisting of 8 to 19 hex characters that - * can be either a nickname or a fingerprint. */ - String searchNickname = ""; - String searchFingerprint = ""; - String searchIPAddress = ""; - SortedSet<String> searchDays = new TreeSet<String>(); - SortedSet<String> searchMonths = new TreeSet<String>(); - SortedSet<Long> searchDayTimestamps = new TreeSet<Long>(); - SortedSet<Long> searchMonthTimestamps = new TreeSet<Long>(); - boolean validQuery = false; - - /* Only parse search parameter if it contains nothing else than - * alphanumeric characters, dots, and spaces. */ - if (alphaNumDotDashDollarSpacePattern.matcher(searchParameter). - matches()) { - SortedSet<String> searchTerms = new TreeSet<String>(); - if (searchParameter.trim().contains(" ")) { - String[] split = searchParameter.trim().split(" "); - for (int i = 0; i < split.length; i++) { - if (split[i].length() > 0) { - searchTerms.add(split[i]); - } - } - } else { - searchTerms.add(searchParameter.trim()); - } - - /* Parse each search term separately. */ - for (String searchTerm : searchTerms) { - - /* If the search term contains a dot, it can only be an IP - * address. */ - if (searchTerm.contains(".") && !searchTerm.startsWith(".")) { - String[] octets = searchTerm.split("\."); - if (searchIPAddress.length() > 0 || octets.length < 2 || - octets.length > 4) { - validQuery = false; - break; - } - boolean invalidOctet = false; - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < octets.length; i++) { - if (!numPattern.matcher(octets[i]).matches() || - octets[i].length() > 3 || - Integer.parseInt(octets[i]) > 255) { - invalidOctet = true; - break; - } else { - sb.append("." + Integer.parseInt(octets[i])); - } - } - if (invalidOctet) { - validQuery = false; - break; - } - if (octets.length < 4) { - sb.append("."); - } - searchIPAddress = sb.toString().substring(1); - validQuery = true; - } - - /* If the search term contains hyphens, it must be a month or a - * day. */ - else if (searchTerm.contains("-") && - searchTerm.startsWith("20")) { - try { - if (searchTerm.length() == 10) { - searchDayTimestamps.add(dayFormat.parse(searchTerm). - getTime()); - searchDays.add(searchTerm); - } else if (searchTerm.length() == 7) { - searchMonthTimestamps.add(monthFormat.parse(searchTerm). - getTime()); - searchMonths.add(searchTerm); - } else { - validQuery = false; - break; - } - } catch (ParseException e) { - validQuery = false; - break; - } - } - - /* If the search term starts with a $ followed by 8 to 40 hex - * characters, it must be a fingerprint. */ - else if ((searchTerm.length() >= 9 && searchTerm.length() <= 41 && - searchTerm.startsWith("$") && - hexPattern.matcher(searchTerm.substring(1)).matches()) || - (searchTerm.length() > 19 && searchTerm.length() <= 40 && - !searchTerm.startsWith("$") && - hexPattern.matcher(searchTerm).matches())) { - if (searchFingerprint.length() > 0) { - validQuery = false; - break; - } - searchFingerprint = searchTerm.substring( - (searchTerm.startsWith("$") ? 1 : 0)); - validQuery = true; - } - - /* If the search term contains up to 19 alphanumerical characters, - * it must be a nickname. */ - else if (searchTerm.length() <= 19 && - alphaNumPattern.matcher(searchTerm).matches()) { - if (searchNickname.length() > 0) { - validQuery = false; - break; - } - searchNickname = searchTerm; - validQuery = true; - } - - /* We didn't recognize this search term. */ - else { - validQuery = false; - break; - } - } - } - - /* We only accept at most one month or three days, but not both, or - * people could accidentally keep the database busy. */ - if (searchDays.size() > 3 || searchMonths.size() > 1 || - (searchMonths.size() == 1 && searchDays.size() > 0)) { - validQuery = false; - } - - /* If the query is invalid, stop here. */ - if (!validQuery) { - request.setAttribute("invalidQuery", "Query is invalid."); - request.getRequestDispatcher("WEB-INF/relay-search.jsp"). - forward(request, response); - return; - } - - /* Look up last consensus in the database. */ - long maxValidAfterMillis = -1L; - try { - long requestedConnection = System.currentTimeMillis(); - Connection conn = this.ds.getConnection(); - String query = "SELECT MAX(validafter) AS last FROM consensus"; - Statement statement = conn.createStatement(); - ResultSet rs = statement.executeQuery(query); - if (rs.next()) { - maxValidAfterMillis = rs.getTimestamp(1).getTime(); - } - rs.close(); - statement.close(); - conn.close(); - this.logger.info("Returned a database connection to the pool " - + "after " + (System.currentTimeMillis() - - requestedConnection) + " millis."); - } catch (SQLException e) { - this.logger.log(Level.WARNING, "Could not look up last consensus " - + "valid-after time in the database.", e); - } - - /* Prepare a string that says what we're searching for. */ - List<String> recognizedSearchTerms = new ArrayList<String>(); - if (searchNickname.length() > 0) { - recognizedSearchTerms.add("nickname <b>" + searchNickname + "</b>"); - } - if (searchFingerprint.length() > 0) { - recognizedSearchTerms.add("fingerprint <b>" + searchFingerprint - + "</b>"); - } - if (searchIPAddress.length() > 0) { - recognizedSearchTerms.add("IP address <b>" + searchIPAddress - + "</b>"); - } - List<String> recognizedIntervals = new ArrayList<String>(); - for (String searchTerm : searchMonths) { - recognizedIntervals.add("in <b>" + searchTerm + "</b>"); - } - for (String searchTerm : searchDays) { - recognizedIntervals.add("on <b>" + searchTerm + "</b>"); - } - StringBuilder searchNoticeBuilder = new StringBuilder(); - if (maxValidAfterMillis > 0L) { - searchNoticeBuilder.append("Most recent consensus in database is " - + "from " + dateTimeFormat.format(maxValidAfterMillis) - + ".</p><p>"); - } - searchNoticeBuilder.append("Searching for relays with "); - if (recognizedSearchTerms.size() == 1) { - searchNoticeBuilder.append(recognizedSearchTerms.get(0)); - } else if (recognizedSearchTerms.size() == 2) { - searchNoticeBuilder.append(recognizedSearchTerms.get(0) + " and " - + recognizedSearchTerms.get(1)); - } else { - for (int i = 0; i < recognizedSearchTerms.size() - 1; i++) { - searchNoticeBuilder.append(recognizedSearchTerms.get(i) + ", "); - } - searchNoticeBuilder.append("and " + recognizedSearchTerms.get( - recognizedSearchTerms.size() - 1)); - } - if (recognizedIntervals.size() == 1) { - searchNoticeBuilder.append(" running " - + recognizedIntervals.get(0)); - } else if (recognizedIntervals.size() == 2) { - searchNoticeBuilder.append(" running " + recognizedIntervals.get(0) - + " and/or " + recognizedIntervals.get(1)); - } else if (recognizedIntervals.size() > 2) { - searchNoticeBuilder.append(" running "); - for (int i = 0; i < recognizedIntervals.size() - 1; i++) { - searchNoticeBuilder.append(recognizedIntervals.get(i) + ", "); - } - searchNoticeBuilder.append("and/or " + recognizedIntervals.get( - recognizedIntervals.size() - 1)); - } - searchNoticeBuilder.append(" ..."); - String searchNotice = searchNoticeBuilder.toString(); - request.setAttribute("searchNotice", searchNotice); - - /* Prepare the query string. */ - StringBuilder conditionBuilder = new StringBuilder(); - boolean addAnd = false; - if (searchNickname.length() > 0) { - conditionBuilder.append((addAnd ? "AND " : "") - + "LOWER(nickname) LIKE '" + searchNickname.toLowerCase() - + "%' "); - addAnd = true; - } - if (searchFingerprint.length() > 0) { - conditionBuilder.append((addAnd ? "AND " : "") - + "fingerprint LIKE '" + searchFingerprint.toLowerCase() - + "%' "); - addAnd = true; - } - if (searchIPAddress.length() > 0) { - conditionBuilder.append((addAnd ? "AND " : "") - + "address LIKE '" + searchIPAddress + "%' "); - addAnd = true; - } - List<String> timeIntervals = new ArrayList<String>(); - if (searchDayTimestamps.size() > 0 || - searchMonthTimestamps.size() > 0) { - StringBuilder timeIntervalBuilder = new StringBuilder(); - boolean addOr = false; - timeIntervalBuilder.append("AND ("); - for (long searchTimestamp : searchDayTimestamps) { - if (searchTimestamp < this.minValidAfterMillis) { - request.setAttribute("outsideInterval", "Returned search " - + "results may be incomplete, as our data only dates back " - + "to " + dateTimeFormat.format(this.minValidAfterMillis) - + ". Older archives are not available."); - } - timeIntervalBuilder.append((addOr ? "OR " : "") - + "(validafter >= '" - + dateTimeFormat.format(searchTimestamp) + "' AND " - + "validafter < '" + dateTimeFormat.format(searchTimestamp - + 24L * 60L * 60L * 1000L) + "') "); - addOr = true; - } - for (long searchTimestamp : searchMonthTimestamps) { - if (searchTimestamp < this.minValidAfterMillis) { - request.setAttribute("outsideInterval", "Returned search " - + "results may be incomplete, as our data only dates back " - + "to " + dateTimeFormat.format(this.minValidAfterMillis) - + ". Older archives are not available."); - } - Calendar firstOfNextMonth = Calendar.getInstance( - TimeZone.getTimeZone("UTC")); - firstOfNextMonth.setTimeInMillis(searchTimestamp); - firstOfNextMonth.add(Calendar.MONTH, 1); - timeIntervalBuilder.append((addOr ? "OR " : "") - + "(validafter >= '" - + dateTimeFormat.format(searchTimestamp) + "' AND " - + "validafter < '" + dateTimeFormat.format( - firstOfNextMonth.getTimeInMillis()) + "') "); - addOr = true; - } - timeIntervalBuilder.append(") "); - timeIntervals.add(timeIntervalBuilder.toString()); - } else { - timeIntervals.add("AND validafter >= '" - + dateTimeFormat.format(System.currentTimeMillis() - - 4L * 24L * 60L * 60L * 1000L) + "' "); - timeIntervals.add("AND validafter >= '" - + dateTimeFormat.format(System.currentTimeMillis() - - 30L * 24L * 60L * 60L * 1000L) + "' "); - } - List<String> queries = new ArrayList<String>(); - for (String timeInterval : timeIntervals) { - StringBuilder queryBuilder = new StringBuilder(); - queryBuilder.append("SELECT validafter, fingerprint, descriptor, " - + "rawdesc FROM statusentry WHERE validafter IN (SELECT " - + "validafter FROM statusentry WHERE "); - queryBuilder.append(conditionBuilder.toString()); - queryBuilder.append(timeInterval); - queryBuilder.append("ORDER BY validafter DESC LIMIT 31) AND "); - queryBuilder.append(conditionBuilder.toString()); - queryBuilder.append(timeInterval); - queries.add(queryBuilder.toString()); - } - - /* Actually execute the query. */ - long startedQuery = System.currentTimeMillis(); - SortedMap<String, SortedSet<String>> foundDescriptors = - new TreeMap<String, SortedSet<String>>( - Collections.reverseOrder()); - Map<String, String> rawValidAfterLines = - new HashMap<String, String>(); - Map<String, String> rawStatusEntries = new HashMap<String, String>(); - String query = null; - int matches = 0; - try { - long requestedConnection = System.currentTimeMillis(); - Connection conn = this.ds.getConnection(); - while (!queries.isEmpty()) { - query = queries.remove(0); - this.logger.info("Running query '" + query + "'."); - Statement statement = conn.createStatement(); - ResultSet rs = statement.executeQuery(query); - while (rs.next()) { - matches++; - String validAfter = rs.getTimestamp(1).toString(). - substring(0, 19); - String fingerprint = rs.getString(2); - if (!foundDescriptors.containsKey(validAfter)) { - foundDescriptors.put(validAfter, new TreeSet<String>()); - } - foundDescriptors.get(validAfter).add(validAfter + " " - + fingerprint); - if (!rawValidAfterLines.containsKey(validAfter)) { - rawValidAfterLines.put(validAfter, "<tt>valid-after " - + "<a href="https://exonerator.torproject.org/" - + "consensus?valid-after=" - + validAfter.replaceAll(":", "-").replaceAll(" ", "-") - + "" target="_blank">" + validAfter + "</a></tt><br>"); - } - byte[] rawStatusEntry = rs.getBytes(4); - String statusEntryLines = null; - try { - statusEntryLines = new String(rawStatusEntry, "US-ASCII"); - } catch (UnsupportedEncodingException e) { - /* This shouldn't happen, because we know that ASCII is - * supported. */ - } - StringBuilder rawStatusEntryBuilder = new StringBuilder(); - String[] lines = statusEntryLines.split("\n"); - for (String line : lines) { - if (line.startsWith("r ")) { - String[] parts = line.split(" "); - String descriptorBase64 = String.format("%040x", - new BigInteger(1, Base64.decodeBase64(parts[3] - + "=="))); - rawStatusEntryBuilder.append("<tt>r " + parts[1] + " " - + parts[2] + " <a href="" - + "https://exonerator.torproject.org/" - + "serverdesc?desc-id=" - + descriptorBase64 + "" target="_blank">" + parts[3] - + "</a> " + parts[4] + " " + parts[5] + " " + parts[6] - + " " + parts[7] + " " + parts[8] + "</tt><br>"); - } else { - rawStatusEntryBuilder.append("<tt>" + line + "</tt><br>"); - } - } - rawStatusEntries.put(validAfter + " " + fingerprint, - rawStatusEntryBuilder.toString()); - } - rs.close(); - statement.close(); - if (matches >= 31) { - queries.clear(); - } - } - conn.close(); - this.logger.info("Returned a database connection to the pool " - + "after " + (System.currentTimeMillis() - - requestedConnection) + " millis."); - } catch (SQLException e) { - - /* Tell the user we have a database problem. */ - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, - "Database problem"); - return; - } - request.setAttribute("query", query); - request.setAttribute("queryTime", System.currentTimeMillis() - - startedQuery); - request.setAttribute("foundDescriptors", foundDescriptors); - request.setAttribute("rawValidAfterLines", rawValidAfterLines); - request.setAttribute("rawStatusEntries", rawStatusEntries); - request.setAttribute("matches", matches); - - /* We're done. Let the JSP do the rest. */ + /* Let the JSP display that we're out of service. */ request.getRequestDispatcher("WEB-INF/relay-search.jsp").forward( request, response); } diff --git a/website/web/WEB-INF/banner.jsp b/website/web/WEB-INF/banner.jsp index 5f6a43f..3acea7f 100644 --- a/website/web/WEB-INF/banner.jsp +++ b/website/web/WEB-INF/banner.jsp @@ -12,8 +12,6 @@ %>class="current"<%} else {%>href="/graphs.html"<%}%>>Graphs</a> <a <% if (currentPage.endsWith("research.jsp")) { %>class="current"<%} else {%>href="/research.html"<%}%>>Research</a> - <a <% if (currentPage.endsWith("status.jsp")) { - %>class="current"<%} else {%>href="/status.html"<%}%>>Status</a> <%if (currentPage.endsWith("graphs.jsp") || currentPage.endsWith("network.jsp") || currentPage.endsWith("bubbles.jsp") || @@ -34,14 +32,6 @@ %>class="current"<%} else {%>href="/performance.html"<%} %>>Performance</a> </font> - <%} else if (currentPage.endsWith("status.jsp") || - currentPage.endsWith("relay-search.jsp")) { - %><br> - <font size="2"> - <a <%if (currentPage.endsWith("relay-search.jsp")){ - %>class="current"<%} else {%>href="/relay-search.html"<%} - %>>Relay Search</a> - </font> <%} else if (currentPage.endsWith("research.jsp") || currentPage.endsWith("data.jsp") || currentPage.endsWith("formats.jsp") || diff --git a/website/web/WEB-INF/error.jsp b/website/web/WEB-INF/error.jsp index 1626035..17ddee3 100644 --- a/website/web/WEB-INF/error.jsp +++ b/website/web/WEB-INF/error.jsp @@ -55,10 +55,6 @@ Maybe you find what you're looking for on our sitemap: <li><a href="tools.html">Tools</a></li> <li><a href="stats.html">Statistics</a></li> </ul></li> -<li><a href="status.html">Status</a> -<ul> -<li><a href="relay-search.html">Relay Search</a></li> -</ul></li> </ul> </p>
diff --git a/website/web/WEB-INF/relay-search.jsp b/website/web/WEB-INF/relay-search.jsp index e6eb630..90b7512 100644 --- a/website/web/WEB-INF/relay-search.jsp +++ b/website/web/WEB-INF/relay-search.jsp @@ -13,73 +13,11 @@ <%@ include file="banner.jsp"%> <div class="main-column"> <h2>Tor Metrics Portal: Relay Search</h2> - <p><font color="red"><b>Notice:</b> This service will be shut down - after June 10, 2014. Possible alternatives are + <p><font color="red">Sorry, this service has been shut down + on June 10, 2014. Possible alternatives are <a href="https://atlas.torproject.org">Atlas</a> and <a href="https://globe.torproject.org">Globe</a>. </font></p> - <p>Search for a relay in the relay descriptor archive by typing - (part of) a <b>nickname</b>, <b>$-prefixed fingerprint</b>, or <b>IP - address</b> and optionally a <b>month (yyyy-mm)</b> or up to three - <b>days (yyyy-mm-dd)</b> in the following search field and - clicking Search. The search will stop after 30 hits or, unless you - provide a month or a day, after parsing the last 30 days of relay - lists.</p> - <br> - <form action="relay-search.html"> - <table> - <tr> - <td><input type="text" name="search" - value="<c:out value="${param.search}"/>"></td> - <td><input type="submit" value="Search"></td> - </tr> - </table> - </form> - <br> - <c:if test="${not empty invalidQuery}"> - <p>Sorry, I didn't understand your query. Please provide a - nickname (e.g., "gabelmoo"), at least the first 8 hex characters - of a fingerprint prefixed by $ (e.g., "$F2044413"), or at least - the first two octets of an IPv4 address in dotted-decimal notation - (e.g., "80.190"). You can also provide at most three months or - days in ISO 8601 format (e.g., "2010-09" or "2010-09-17").</p> - </c:if> - <c:if test="${not empty outsideInterval}"> - <p>${outsideInterval}</p> - </c:if> - <c:if test="${not empty searchNotice}"> - <p>${searchNotice}</p> - </c:if> - <c:if test="${not empty query}"> - <!-- ${query} --> - </c:if> - <c:if test="${not empty queryTime}"> - <c:forEach var="consensus" items="${foundDescriptors}"> - ${rawValidAfterLines[consensus.key]} - <c:forEach var="statusentry" items="${consensus.value}"> - ${rawStatusEntries[statusentry]} - </c:forEach> - <br> - </c:forEach> - <p>Found - <c:choose> - <c:when test="${matches > 30}"> - more than 30 relays (displaying only those in the last - consensuses) - </c:when> - <c:otherwise> - ${matches} relays - </c:otherwise> - </c:choose> - in <fmt:formatNumber value="${queryTime / 1000}" pattern="#.###"/> - seconds.</p> - <c:if test="${queryTime > 10000}"> - <p>In theory, search time should not exceed 10 seconds. The - query was '${query}'. If this or similar searches remain slow, - please <a href="mailto:tor-assistants@torproject.org">let us - know</a>!</p> - </c:if> - </c:if> </div> </div> <div class="bottom" id="bottom">
tor-commits@lists.torproject.org