[tor-commits] [onionoo/master] Perform reverse DNS lookups to learn relay host names.
karsten at torproject.org
karsten at torproject.org
Mon Jun 18 14:11:27 UTC 2012
commit b1cf0cab52b7155446c77b98cb6f7d696a053ee3
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date: Mon Jun 18 16:10:21 2012 +0200
Perform reverse DNS lookups to learn relay host names.
Implements #5247.
---
src/org/torproject/onionoo/CurrentNodes.java | 33 +++++--
src/org/torproject/onionoo/DetailDataWriter.java | 115 ++++++++++++++++++++++
src/org/torproject/onionoo/Main.java | 2 +
src/org/torproject/onionoo/Node.java | 18 +++-
web/index.html | 8 ++
5 files changed, 165 insertions(+), 11 deletions(-)
diff --git a/src/org/torproject/onionoo/CurrentNodes.java b/src/org/torproject/onionoo/CurrentNodes.java
index 4da7133..0cfcbf6 100644
--- a/src/org/torproject/onionoo/CurrentNodes.java
+++ b/src/org/torproject/onionoo/CurrentNodes.java
@@ -98,16 +98,22 @@ public class CurrentNodes {
if (parts.length > 10) {
countryCode = parts[10];
}
+ String hostName = null;
+ long lastRdnsLookup = -1L;
+ if (parts.length > 12) {
+ hostName = parts[11].equals("null") ? null : parts[11];
+ lastRdnsLookup = Long.parseLong(parts[12]);
+ }
if (isRelay) {
this.addRelay(nickname, fingerprint, address,
orAddressesAndPorts, exitAddresses,
publishedOrValidAfterMillis, orPort, dirPort, relayFlags,
- consensusWeight, countryCode);
+ consensusWeight, countryCode, hostName, lastRdnsLookup);
} else {
this.addBridge(nickname, fingerprint, address,
orAddressesAndPorts, exitAddresses,
publishedOrValidAfterMillis, orPort, dirPort, relayFlags,
- consensusWeight, countryCode);
+ consensusWeight, countryCode, hostName, lastRdnsLookup);
}
}
br.close();
@@ -166,10 +172,14 @@ public class CurrentNodes {
entry.getConsensusWeight());
String countryCode = entry.getCountryCode() != null
? entry.getCountryCode() : "??";
+ String hostName = entry.getHostName() != null
+ ? entry.getHostName() : "null";
+ long lastRdnsLookup = entry.getLastRdnsLookup();
bw.write("r " + nickname + " " + fingerprint + " "
+ addressesBuilder.toString() + " " + validAfter + " "
+ orPort + " " + dirPort + " " + relayFlags + " "
- + consensusWeight + " " + countryCode + "\n");
+ + consensusWeight + " " + countryCode + " " + hostName + " "
+ + String.valueOf(lastRdnsLookup) + "\n");
}
for (Node entry : this.currentBridges.values()) {
String nickname = entry.getNickname();
@@ -194,7 +204,7 @@ public class CurrentNodes {
String relayFlags = sb.toString().substring(1);
bw.write("b " + nickname + " " + fingerprint + " "
+ addressesBuilder.toString() + " " + published + " " + orPort
- + " " + dirPort + " " + relayFlags + " -1 ??\n");
+ + " " + dirPort + " " + relayFlags + " -1 ?? null -1\n");
}
bw.close();
} catch (IOException e) {
@@ -256,7 +266,7 @@ public class CurrentNodes {
long consensusWeight = entry.getBandwidth();
this.addRelay(nickname, fingerprint, address, orAddressesAndPorts,
null, validAfterMillis, orPort, dirPort, relayFlags,
- consensusWeight, null);
+ consensusWeight, null, null, -1L);
}
}
@@ -264,14 +274,15 @@ public class CurrentNodes {
String address, SortedSet<String> orAddressesAndPorts,
SortedSet<String> exitAddresses, long validAfterMillis, int orPort,
int dirPort, SortedSet<String> relayFlags, long consensusWeight,
- String countryCode) {
+ String countryCode, String hostname, long lastRdnsLookup) {
if (validAfterMillis >= cutoff &&
(!this.currentRelays.containsKey(fingerprint) ||
this.currentRelays.get(fingerprint).getLastSeenMillis() <
validAfterMillis)) {
Node entry = new Node(nickname, fingerprint, address,
orAddressesAndPorts, exitAddresses, validAfterMillis, orPort,
- dirPort, relayFlags, consensusWeight, countryCode);
+ dirPort, relayFlags, consensusWeight, countryCode, hostname,
+ lastRdnsLookup);
this.currentRelays.put(fingerprint, entry);
if (validAfterMillis > this.lastValidAfterMillis) {
this.lastValidAfterMillis = validAfterMillis;
@@ -368,7 +379,8 @@ public class CurrentNodes {
int dirPort = entry.getDirPort();
SortedSet<String> relayFlags = entry.getFlags();
this.addBridge(nickname, fingerprint, address, orAddressesAndPorts,
- null, publishedMillis, orPort, dirPort, relayFlags, -1, "??");
+ null, publishedMillis, orPort, dirPort, relayFlags, -1, "??",
+ null, -1L);
}
}
@@ -376,14 +388,15 @@ public class CurrentNodes {
String address, SortedSet<String> orAddressesAndPorts,
SortedSet<String> exitAddresses, long publishedMillis, int orPort,
int dirPort, SortedSet<String> relayFlags, long consensusWeight,
- String countryCode) {
+ String countryCode, String hostname, long lastRdnsLookup) {
if (publishedMillis >= cutoff &&
(!this.currentBridges.containsKey(fingerprint) ||
this.currentBridges.get(fingerprint).getLastSeenMillis() <
publishedMillis)) {
Node entry = new Node(nickname, fingerprint, address,
orAddressesAndPorts, exitAddresses, publishedMillis, orPort,
- dirPort, relayFlags, consensusWeight, countryCode);
+ dirPort, relayFlags, consensusWeight, countryCode, hostname,
+ lastRdnsLookup);
this.currentBridges.put(fingerprint, entry);
if (publishedMillis > this.lastPublishedMillis) {
this.lastPublishedMillis = publishedMillis;
diff --git a/src/org/torproject/onionoo/DetailDataWriter.java b/src/org/torproject/onionoo/DetailDataWriter.java
index 6a724d8..3d03981 100644
--- a/src/org/torproject/onionoo/DetailDataWriter.java
+++ b/src/org/torproject/onionoo/DetailDataWriter.java
@@ -8,11 +8,15 @@ import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
@@ -50,6 +54,112 @@ public class DetailDataWriter {
this.bridges = currentBridges;
}
+ private static final long RDNS_LOOKUP_MAX_REQUEST_MILLIS = 10L * 1000L;
+ private static final long RDNS_LOOKUP_MAX_DURATION_MILLIS = 5L * 60L
+ * 1000L;
+ private static final long RDNS_LOOKUP_MAX_AGE_MILLIS = 12L * 60L * 60L
+ * 1000L;
+ private static final int RDNS_LOOKUP_WORKERS_NUM = 5;
+ private Set<String> rdnsLookupJobs;
+ private Map<String, String> rdnsLookupResults;
+ private long startedRdnsLookups;
+ private List<RdnsLookupWorker> rdnsLookupWorkers;
+ public void startReverseDomainNameLookups() {
+ this.startedRdnsLookups = System.currentTimeMillis();
+ this.rdnsLookupJobs = new HashSet<String>();
+ for (Node relay : relays.values()) {
+ if (relay.getLastRdnsLookup() < this.startedRdnsLookups
+ - RDNS_LOOKUP_MAX_AGE_MILLIS) {
+ this.rdnsLookupJobs.add(relay.getAddress());
+ }
+ }
+ this.rdnsLookupResults = new HashMap<String, String>();
+ this.rdnsLookupWorkers = new ArrayList<RdnsLookupWorker>();
+ for (int i = 0; i < RDNS_LOOKUP_WORKERS_NUM; i++) {
+ RdnsLookupWorker rdnsLookupWorker = new RdnsLookupWorker();
+ this.rdnsLookupWorkers.add(rdnsLookupWorker);
+ rdnsLookupWorker.setDaemon(true);
+ rdnsLookupWorker.start();
+ }
+ }
+
+ public void finishReverseDomainNameLookups() {
+ for (RdnsLookupWorker rdnsLookupWorker : this.rdnsLookupWorkers) {
+ try {
+ rdnsLookupWorker.join();
+ } catch (InterruptedException e) {
+ /* This is not something that we can take care of. Just leave the
+ * worker thread alone. */
+ }
+ }
+ synchronized (this.rdnsLookupResults) {
+ for (Node relay : relays.values()) {
+ if (this.rdnsLookupResults.containsKey(relay.getAddress())) {
+ relay.setHostName(this.rdnsLookupResults.get(
+ relay.getAddress()));
+ relay.setLastRdnsLookup(this.startedRdnsLookups);
+ }
+ }
+ }
+ }
+
+ private class RdnsLookupWorker extends Thread {
+ public void run() {
+ while (System.currentTimeMillis() - RDNS_LOOKUP_MAX_DURATION_MILLIS
+ <= startedRdnsLookups) {
+ String rdnsLookupJob = null;
+ synchronized (rdnsLookupJobs) {
+ for (String job : rdnsLookupJobs) {
+ rdnsLookupJob = job;
+ rdnsLookupJobs.remove(job);
+ break;
+ }
+ }
+ if (rdnsLookupJob == null) {
+ break;
+ }
+ RdnsLookupRequest request = new RdnsLookupRequest(this,
+ rdnsLookupJob);
+ request.setDaemon(true);
+ request.start();
+ try {
+ Thread.sleep(RDNS_LOOKUP_MAX_REQUEST_MILLIS);
+ } catch (InterruptedException e) {
+ /* Getting interrupted should be the default case. */
+ }
+ String hostName = request.getHostName();
+ if (hostName != null) {
+ synchronized (rdnsLookupResults) {
+ rdnsLookupResults.put(rdnsLookupJob, hostName);
+ }
+ }
+ }
+ }
+ }
+
+ private class RdnsLookupRequest extends Thread {
+ RdnsLookupWorker parent;
+ String address, hostName;
+ public RdnsLookupRequest(RdnsLookupWorker parent, String address) {
+ this.parent = parent;
+ this.address = address;
+ }
+ public void run() {
+ try {
+ String result = InetAddress.getByName(this.address).getHostName();
+ synchronized (this) {
+ this.hostName = result;
+ }
+ } catch (UnknownHostException e) {
+ /* We'll try again the next time. */
+ }
+ this.parent.interrupt();
+ }
+ public synchronized String getHostName() {
+ return hostName;
+ }
+ }
+
private Map<String, ServerDescriptor> relayServerDescriptors =
new HashMap<String, ServerDescriptor>();
public void readRelayServerDescriptors() {
@@ -331,6 +441,7 @@ public class DetailDataWriter {
String aSNumber = entry.getASNumber();
String aSName = entry.getASName();
long consensusWeight = entry.getConsensusWeight();
+ String hostName = entry.getHostName();
StringBuilder sb = new StringBuilder();
sb.append("{\"version\":1,\n"
+ "\"nickname\":\"" + nickname + "\",\n"
@@ -381,6 +492,10 @@ public class DetailDataWriter {
sb.append(",\n\"consensus_weight\":"
+ String.valueOf(consensusWeight));
}
+ if (hostName != null) {
+ sb.append(",\n\"host_name\":\""
+ + StringEscapeUtils.escapeJavaScript(hostName) + "\"");
+ }
/* Add exit addresses if at least one of them is distinct from the
* onion-routing addresses. */
diff --git a/src/org/torproject/onionoo/Main.java b/src/org/torproject/onionoo/Main.java
index 2c906db..5cac8de 100644
--- a/src/org/torproject/onionoo/Main.java
+++ b/src/org/torproject/onionoo/Main.java
@@ -22,10 +22,12 @@ public class Main {
DetailDataWriter ddw = new DetailDataWriter();
ddw.setCurrentRelays(cn.getCurrentRelays());
ddw.setCurrentBridges(cn.getCurrentBridges());
+ ddw.startReverseDomainNameLookups();
ddw.readRelayServerDescriptors();
ddw.readExitLists();
ddw.readBridgeServerDescriptors();
ddw.readBridgePoolAssignments();
+ ddw.finishReverseDomainNameLookups();
ddw.writeDetailDataFiles();
printStatus("Updating bandwidth data.");
diff --git a/src/org/torproject/onionoo/Node.java b/src/org/torproject/onionoo/Node.java
index 0bb4a23..88ca71c 100644
--- a/src/org/torproject/onionoo/Node.java
+++ b/src/org/torproject/onionoo/Node.java
@@ -33,11 +33,13 @@ public class Node {
private SortedSet<String> relayFlags;
private long consensusWeight;
private boolean running;
+ private String hostName;
+ private long lastRdnsLookup = -1L;
public Node(String nickname, String fingerprint, String address,
SortedSet<String> orAddressesAndPorts,
SortedSet<String> exitAddresses, long lastSeenMillis, int orPort,
int dirPort, SortedSet<String> relayFlags, long consensusWeight,
- String countryCode) {
+ String countryCode, String hostName, long lastRdnsLookup) {
this.nickname = nickname;
this.fingerprint = fingerprint;
try {
@@ -66,6 +68,8 @@ public class Node {
this.relayFlags = relayFlags;
this.consensusWeight = consensusWeight;
this.countryCode = countryCode;
+ this.hostName = hostName;
+ this.lastRdnsLookup = lastRdnsLookup;
}
public String getFingerprint() {
return this.fingerprint;
@@ -178,5 +182,17 @@ public class Node {
public boolean getRunning() {
return this.running;
}
+ public void setHostName(String hostName) {
+ this.hostName = hostName;
+ }
+ public String getHostName() {
+ return this.hostName;
+ }
+ public void setLastRdnsLookup(long lastRdnsLookup) {
+ this.lastRdnsLookup = lastRdnsLookup;
+ }
+ public long getLastRdnsLookup() {
+ return this.lastRdnsLookup;
+ }
}
diff --git a/web/index.html b/web/index.html
index d9ac6e8..fe937de 100755
--- a/web/index.html
+++ b/web/index.html
@@ -247,6 +247,14 @@ the directory authorities.
The unit is arbitrary; currently it's kilobytes per second, but that might
change in the future.
Required field.</li>
+<li><b><font color="blue">"host_name":</font></b> Host name as found in a
+reverse DNS lookup of the relay IP address.
+This field is updated at most once in 12 hours, unless the relay IP
+address changes.
+Optional field.
+Omitted if the relay IP address was not looked up or if no lookup request
+was successful yet.
+<font color="blue">Added field on June 18, 2012.</font></li>
<li><b>"last_restarted":</b> UTC timestamp (YYYY-MM-DD hh:mm:ss) when the
relay was last (re-)started.
Optional field.</li>
More information about the tor-commits
mailing list