[tor-commits] [onionoo/master] Add two new parameters "as" and "flag".
karsten at torproject.org
karsten at torproject.org
Tue Apr 9 03:32:29 UTC 2013
commit ef51f1b4d6a44699177c2c09d19bfa8e6c2bac9e
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date: Tue Apr 9 05:32:19 2013 +0200
Add two new parameters "as" and "flag".
Also fix a potential bug in the servlet's filtering and sorting code.
It's unclear whether this really was a bug, but let's clean up the code
just in case.
---
src/org/torproject/onionoo/CurrentNodes.java | 29 +++--
src/org/torproject/onionoo/Node.java | 3 +-
src/org/torproject/onionoo/ResourceServlet.java | 153 +++++++++++++++++++----
web/index.html | 16 +++
4 files changed, 168 insertions(+), 33 deletions(-)
diff --git a/src/org/torproject/onionoo/CurrentNodes.java b/src/org/torproject/onionoo/CurrentNodes.java
index d98eb4e..f2bbac7 100644
--- a/src/org/torproject/onionoo/CurrentNodes.java
+++ b/src/org/torproject/onionoo/CurrentNodes.java
@@ -124,20 +124,24 @@ public class CurrentNodes {
lastChangedAddresses = dateTimeFormat.parse(parts[17] + " "
+ parts[18]).getTime();
}
+ String aSNumber = null;
+ if (parts.length > 19) {
+ aSNumber = parts[19];
+ }
if (isRelay) {
this.addRelay(nickname, fingerprint, address,
orAddressesAndPorts, exitAddresses,
publishedOrValidAfterMillis, orPort, dirPort, relayFlags,
consensusWeight, countryCode, hostName, lastRdnsLookup,
defaultPolicy, portList, firstSeenMillis,
- lastChangedAddresses);
+ lastChangedAddresses, aSNumber);
} else {
this.addBridge(nickname, fingerprint, address,
orAddressesAndPorts, exitAddresses,
publishedOrValidAfterMillis, orPort, dirPort, relayFlags,
consensusWeight, countryCode, hostName, lastRdnsLookup,
defaultPolicy, portList, firstSeenMillis,
- lastChangedAddresses);
+ lastChangedAddresses, aSNumber);
}
}
br.close();
@@ -212,13 +216,15 @@ public class CurrentNodes {
entry.getFirstSeenMillis());
String lastChangedAddresses = dateTimeFormat.format(
entry.getLastChangedOrAddress());
+ String aSNumber = entry.getASNumber() != null
+ ? entry.getASNumber() : "null";
bw.write("r " + nickname + " " + fingerprint + " "
+ addressesBuilder.toString() + " " + lastSeen + " "
+ orPort + " " + dirPort + " " + flagsBuilder.toString() + " "
+ consensusWeight + " " + countryCode + " " + hostName + " "
+ String.valueOf(lastRdnsLookup) + " " + defaultPolicy + " "
+ portList + " " + firstSeen + " " + lastChangedAddresses
- + "\n");
+ + " " + aSNumber + "\n");
}
for (Node entry : this.currentBridges.values()) {
String nickname = entry.getNickname();
@@ -246,7 +252,8 @@ public class CurrentNodes {
bw.write("b " + nickname + " " + fingerprint + " "
+ addressesBuilder.toString() + " " + published + " " + orPort
+ " " + dirPort + " " + flagsBuilder.toString()
- + " -1 ?? null -1 null null " + firstSeen + " null null\n");
+ + " -1 ?? null -1 null null " + firstSeen + " null null "
+ + "null\n");
}
bw.close();
} catch (IOException e) {
@@ -317,7 +324,7 @@ public class CurrentNodes {
this.addRelay(nickname, fingerprint, address, orAddressesAndPorts,
null, validAfterMillis, orPort, dirPort, relayFlags,
consensusWeight, null, null, -1L, defaultPolicy, portList,
- validAfterMillis, validAfterMillis);
+ validAfterMillis, validAfterMillis, null);
}
if (this.lastValidAfterMillis == validAfterMillis) {
this.lastBandwidthWeights = consensus.getBandwidthWeights();
@@ -330,7 +337,7 @@ public class CurrentNodes {
int dirPort, SortedSet<String> relayFlags, long consensusWeight,
String countryCode, String hostName, long lastRdnsLookup,
String defaultPolicy, String portList, long firstSeenMillis,
- long lastChangedAddresses) {
+ long lastChangedAddresses, String aSNumber) {
/* Remember addresses and OR/dir ports that the relay advertised at
* the given time. */
SortedMap<Long, Set<String>> lastAddresses =
@@ -376,7 +383,7 @@ public class CurrentNodes {
orAddressesAndPorts, exitAddresses, lastSeenMillis, orPort,
dirPort, relayFlags, consensusWeight, countryCode, hostName,
lastRdnsLookup, defaultPolicy, portList, firstSeenMillis,
- lastAddresses);
+ lastAddresses, aSNumber);
this.currentRelays.put(fingerprint, entry);
/* If this entry comes from a new consensus, update our global last
* valid-after time. */
@@ -769,7 +776,7 @@ public class CurrentNodes {
SortedSet<String> relayFlags = entry.getFlags();
this.addBridge(nickname, fingerprint, address, orAddressesAndPorts,
null, publishedMillis, orPort, dirPort, relayFlags, -1, "??",
- null, -1L, null, null, publishedMillis, -1L);
+ null, -1L, null, null, publishedMillis, -1L, null);
}
}
@@ -779,7 +786,7 @@ public class CurrentNodes {
int dirPort, SortedSet<String> relayFlags, long consensusWeight,
String countryCode, String hostname, long lastRdnsLookup,
String defaultPolicy, String portList, long firstSeenMillis,
- long lastChangedAddresses) {
+ long lastChangedAddresses, String aSNumber) {
/* See if there's already an entry for this bridge. */
if (this.currentBridges.containsKey(fingerprint)) {
Node existingEntry = this.currentBridges.get(fingerprint);
@@ -797,6 +804,7 @@ public class CurrentNodes {
countryCode = existingEntry.getCountryCode();
defaultPolicy = existingEntry.getDefaultPolicy();
portList = existingEntry.getPortList();
+ aSNumber = existingEntry.getASNumber();
}
/* Update relay-history fields. */
firstSeenMillis = Math.min(firstSeenMillis,
@@ -806,7 +814,8 @@ public class CurrentNodes {
Node entry = new Node(nickname, fingerprint, address,
orAddressesAndPorts, exitAddresses, lastSeenMillis, orPort,
dirPort, relayFlags, consensusWeight, countryCode, hostname,
- lastRdnsLookup, defaultPolicy, portList, firstSeenMillis, null);
+ lastRdnsLookup, defaultPolicy, portList, firstSeenMillis, null,
+ aSNumber);
this.currentBridges.put(fingerprint, entry);
/* If this entry comes from a new status, update our global last
* published time. */
diff --git a/src/org/torproject/onionoo/Node.java b/src/org/torproject/onionoo/Node.java
index 0cadd38..35d81c3 100644
--- a/src/org/torproject/onionoo/Node.java
+++ b/src/org/torproject/onionoo/Node.java
@@ -54,7 +54,7 @@ public class Node {
int dirPort, SortedSet<String> relayFlags, long consensusWeight,
String countryCode, String hostName, long lastRdnsLookup,
String defaultPolicy, String portList, long firstSeenMillis,
- SortedMap<Long, Set<String>> lastAddresses) {
+ SortedMap<Long, Set<String>> lastAddresses, String aSNumber) {
this.nickname = nickname;
this.fingerprint = fingerprint;
try {
@@ -89,6 +89,7 @@ public class Node {
this.portList = portList;
this.firstSeenMillis = firstSeenMillis;
this.lastAddresses = lastAddresses;
+ this.aSNumber = aSNumber;
}
public String getFingerprint() {
return this.fingerprint;
diff --git a/src/org/torproject/onionoo/ResourceServlet.java b/src/org/torproject/onionoo/ResourceServlet.java
index 0fa9174..19dc752 100644
--- a/src/org/torproject/onionoo/ResourceServlet.java
+++ b/src/org/torproject/onionoo/ResourceServlet.java
@@ -45,18 +45,25 @@ public class ResourceServlet extends HttpServlet {
long summaryFileLastModified = -1L;
boolean readSummaryFile = false;
private String relaysPublishedString, bridgesPublishedString;
- private List<String> relaysByConsensusWeight = new ArrayList<String>();
- private Map<String, String>
- relayFingerprintSummaryLines = new HashMap<String, String>(),
- bridgeFingerprintSummaryLines = new HashMap<String, String>();
- private Map<String, Set<String>> relaysByCountryCode =
- new HashMap<String, Set<String>>();
+ private List<String> relaysByConsensusWeight = null;
+ private Map<String, String> relayFingerprintSummaryLines = null,
+ bridgeFingerprintSummaryLines = null;
+ private Map<String, Set<String>> relaysByCountryCode = null,
+ relaysByASNumber = null, relaysByFlag = null;
private void readSummaryFile() {
if (!summaryFile.exists()) {
readSummaryFile = false;
return;
}
if (summaryFile.lastModified() > this.summaryFileLastModified) {
+ List<String> relaysByConsensusWeight = new ArrayList<String>();
+ Map<String, String>
+ relayFingerprintSummaryLines = new HashMap<String, String>(),
+ bridgeFingerprintSummaryLines = new HashMap<String, String>();
+ Map<String, Set<String>>
+ relaysByCountryCode = new HashMap<String, Set<String>>(),
+ relaysByASNumber = new HashMap<String, Set<String>>(),
+ relaysByFlag = new HashMap<String, Set<String>>();
CurrentNodes cn = new CurrentNodes();
cn.readRelaySearchDataFile(this.summaryFile);
cn.setRelayRunningBits();
@@ -74,38 +81,57 @@ public class ResourceServlet extends HttpServlet {
String hashedFingerprint = entry.getHashedFingerprint().
toUpperCase();
String line = this.formatRelaySummaryLine(entry);
- this.relayFingerprintSummaryLines.put(fingerprint, line);
- this.relayFingerprintSummaryLines.put(hashedFingerprint, line);
+ relayFingerprintSummaryLines.put(fingerprint, line);
+ relayFingerprintSummaryLines.put(hashedFingerprint, line);
long consensusWeight = entry.getConsensusWeight();
orderRelaysByConsensusWeight.add(String.format("%020d %s",
consensusWeight, fingerprint));
orderRelaysByConsensusWeight.add(String.format("%020d %s",
consensusWeight, hashedFingerprint));
- String countryCode = entry.getCountryCode();
- if (countryCode == null) {
- countryCode = "??";
+ if (entry.getCountryCode() != null) {
+ String countryCode = entry.getCountryCode();
+ if (!relaysByCountryCode.containsKey(countryCode)) {
+ relaysByCountryCode.put(countryCode, new HashSet<String>());
+ }
+ relaysByCountryCode.get(countryCode).add(fingerprint);
+ relaysByCountryCode.get(countryCode).add(hashedFingerprint);
+ }
+ if (entry.getASNumber() != null) {
+ String aSNumber = entry.getASNumber();
+ if (!relaysByASNumber.containsKey(aSNumber)) {
+ relaysByASNumber.put(aSNumber, new HashSet<String>());
+ }
+ relaysByASNumber.get(aSNumber).add(fingerprint);
+ relaysByASNumber.get(aSNumber).add(hashedFingerprint);
}
- if (!this.relaysByCountryCode.containsKey(countryCode)) {
- this.relaysByCountryCode.put(countryCode,
- new HashSet<String>());
+ for (String flag : entry.getRelayFlags()) {
+ String flagLowerCase = flag.toLowerCase();
+ if (!relaysByFlag.containsKey(flagLowerCase)) {
+ relaysByFlag.put(flagLowerCase, new HashSet<String>());
+ }
+ relaysByFlag.get(flagLowerCase).add(fingerprint);
+ relaysByFlag.get(flagLowerCase).add(hashedFingerprint);
}
- this.relaysByCountryCode.get(countryCode).add(fingerprint);
- this.relaysByCountryCode.get(countryCode).add(hashedFingerprint);
}
Collections.sort(orderRelaysByConsensusWeight);
- this.relaysByConsensusWeight = new ArrayList<String>();
+ relaysByConsensusWeight = new ArrayList<String>();
for (String relay : orderRelaysByConsensusWeight) {
- this.relaysByConsensusWeight.add(relay.split(" ")[1]);
+ relaysByConsensusWeight.add(relay.split(" ")[1]);
}
for (Node entry : cn.getCurrentBridges().values()) {
String hashedFingerprint = entry.getFingerprint().toUpperCase();
String hashedHashedFingerprint = entry.getHashedFingerprint().
toUpperCase();
String line = this.formatBridgeSummaryLine(entry);
- this.bridgeFingerprintSummaryLines.put(hashedFingerprint, line);
- this.bridgeFingerprintSummaryLines.put(hashedHashedFingerprint,
- line);
+ bridgeFingerprintSummaryLines.put(hashedFingerprint, line);
+ bridgeFingerprintSummaryLines.put(hashedHashedFingerprint, line);
}
+ this.relaysByConsensusWeight = relaysByConsensusWeight;
+ this.relayFingerprintSummaryLines = relayFingerprintSummaryLines;
+ this.bridgeFingerprintSummaryLines = bridgeFingerprintSummaryLines;
+ this.relaysByCountryCode = relaysByCountryCode;
+ this.relaysByASNumber = relaysByASNumber;
+ this.relaysByFlag = relaysByFlag;
}
this.summaryFileLastModified = summaryFile.lastModified();
this.readSummaryFile = true;
@@ -185,7 +211,7 @@ public class ResourceServlet extends HttpServlet {
/* Make sure that the request doesn't contain any unknown
* parameters. */
Set<String> knownParameters = new HashSet<String>(Arrays.asList(
- "type,running,search,lookup,country,order,limit,offset".
+ "type,running,search,lookup,country,as,flag,order,limit,offset".
split(",")));
for (String parameterKey : parameterMap.keySet()) {
if (!knownParameters.contains(parameterKey)) {
@@ -253,6 +279,25 @@ public class ResourceServlet extends HttpServlet {
this.filterByCountryCode(filteredRelays, filteredBridges,
countryCodeParameter);
}
+ if (parameterMap.containsKey("as")) {
+ String aSNumberParameter = this.parseASNumberParameter(
+ parameterMap.get("as"));
+ if (aSNumberParameter == null) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ this.filterByASNumber(filteredRelays, filteredBridges,
+ aSNumberParameter);
+ }
+ if (parameterMap.containsKey("flag")) {
+ String flagParameter = this.parseFlagParameter(
+ parameterMap.get("flag"));
+ if (flagParameter == null) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ this.filterByFlag(filteredRelays, filteredBridges, flagParameter);
+ }
/* Re-order and limit results. */
List<String> orderedRelays = new ArrayList<String>();
@@ -390,6 +435,24 @@ public class ResourceServlet extends HttpServlet {
return parameter;
}
+ private static Pattern aSNumberParameterPattern =
+ Pattern.compile("^[asAS]{0,2}[0-9]{1,10}$");
+ private String parseASNumberParameter(String parameter) {
+ if (!aSNumberParameterPattern.matcher(parameter).matches()) {
+ return null;
+ }
+ return parameter;
+ }
+
+ private static Pattern flagPattern =
+ Pattern.compile("^[a-zA-Z]{1,20}$");
+ private String parseFlagParameter(String parameter) {
+ if (!flagPattern.matcher(parameter).matches()) {
+ return null;
+ }
+ return parameter;
+ }
+
private void filterByType(Map<String, String> filteredRelays,
Map<String, String> filteredBridges, boolean relaysRequested) {
if (relaysRequested) {
@@ -540,6 +603,52 @@ public class ResourceServlet extends HttpServlet {
filteredBridges.clear();
}
+ private void filterByASNumber(Map<String, String> filteredRelays,
+ Map<String, String> filteredBridges, String aSNumberParameter) {
+ String aSNumber = aSNumberParameter.toUpperCase();
+ if (!aSNumber.startsWith("AS")) {
+ aSNumber = "AS" + aSNumber;
+ }
+ if (!this.relaysByASNumber.containsKey(aSNumber)) {
+ filteredRelays.clear();
+ } else {
+ Set<String> relaysWithASNumber =
+ this.relaysByASNumber.get(aSNumber);
+ Set<String> removeRelays = new HashSet<String>();
+ for (Map.Entry<String, String> e : filteredRelays.entrySet()) {
+ String fingerprint = e.getKey();
+ if (!relaysWithASNumber.contains(fingerprint)) {
+ removeRelays.add(fingerprint);
+ }
+ }
+ for (String fingerprint : removeRelays) {
+ filteredRelays.remove(fingerprint);
+ }
+ }
+ filteredBridges.clear();
+ }
+
+ private void filterByFlag(Map<String, String> filteredRelays,
+ Map<String, String> filteredBridges, String flagParameter) {
+ String flag = flagParameter.toLowerCase();
+ if (!this.relaysByFlag.containsKey(flag)) {
+ filteredRelays.clear();
+ } else {
+ Set<String> relaysWithFlag = this.relaysByFlag.get(flag);
+ Set<String> removeRelays = new HashSet<String>();
+ for (Map.Entry<String, String> e : filteredRelays.entrySet()) {
+ String fingerprint = e.getKey();
+ if (!relaysWithFlag.contains(fingerprint)) {
+ removeRelays.add(fingerprint);
+ }
+ }
+ for (String fingerprint : removeRelays) {
+ filteredRelays.remove(fingerprint);
+ }
+ }
+ filteredBridges.clear();
+ }
+
private void writeRelays(List<String> relays, PrintWriter pw,
String resourceType) {
pw.write("{\"relays_published\":\"" + this.relaysPublishedString
diff --git a/web/index.html b/web/index.html
index e3bbdb7..152cec6 100755
--- a/web/index.html
+++ b/web/index.html
@@ -660,6 +660,22 @@ Lookups are case-insensitive.
given country as identified by a two-letter country code.
Filtering by country code is case-insensitive.
</td></tr>
+<tr><td><b><font color="blue">as</font></b></td>
+<td>Return only relays which are located in the
+given autonomous system (AS) as identified by the AS number (with or
+without preceding "AS" part).
+Filtering by AS number is case-insensitive.
+<font color="blue">Added on April 9.</font>
+</td></tr>
+<tr><td><b><font color="blue">flag</font></b></td>
+<td>Return only relays or bridges which have the
+given relay flag assigned by the directory authorities or the bridge
+authority.
+Note that if the flag parameter is specified more than once, only the
+first parameter value will be considered.
+Filtering by flag is case-insensitive.
+<font color="blue">Added on April 9.</font>
+</td></tr>
</table>
<p>Relay and/or bridge documents in the response can be ordered and
limited by providing further parameters.
More information about the tor-commits
mailing list