commit 40a2faee73ee002c90582e16008b2211c2d1bb8f Author: Karsten Loesing karsten.loesing@gmx.net Date: Wed Dec 5 13:49:31 2012 +0100
Add "[last|first]_seen" fields to details docs.
Implements step 1 of 3 as suggested in #6509. --- src/org/torproject/onionoo/CurrentNodes.java | 40 ++++++++++++++++----- src/org/torproject/onionoo/DetailDataWriter.java | 13 ++++++- src/org/torproject/onionoo/Node.java | 7 +++- web/index.html | 28 +++++++++++++++ 4 files changed, 75 insertions(+), 13 deletions(-)
diff --git a/src/org/torproject/onionoo/CurrentNodes.java b/src/org/torproject/onionoo/CurrentNodes.java index 82ba592..9e5d0db 100644 --- a/src/org/torproject/onionoo/CurrentNodes.java +++ b/src/org/torproject/onionoo/CurrentNodes.java @@ -113,18 +113,23 @@ public class CurrentNodes { portList = parts[14]; } } + long firstSeenMillis = publishedOrValidAfterMillis; + if (parts.length > 16) { + firstSeenMillis = dateTimeFormat.parse(parts[15] + " " + + parts[16]).getTime(); + } if (isRelay) { this.addRelay(nickname, fingerprint, address, orAddressesAndPorts, exitAddresses, publishedOrValidAfterMillis, orPort, dirPort, relayFlags, consensusWeight, countryCode, hostName, lastRdnsLookup, - defaultPolicy, portList); + defaultPolicy, portList, firstSeenMillis); } else { this.addBridge(nickname, fingerprint, address, orAddressesAndPorts, exitAddresses, publishedOrValidAfterMillis, orPort, dirPort, relayFlags, consensusWeight, countryCode, hostName, lastRdnsLookup, - defaultPolicy, portList); + defaultPolicy, portList, firstSeenMillis); } } br.close(); @@ -190,12 +195,14 @@ public class CurrentNodes { ? entry.getDefaultPolicy() : "null"; String portList = entry.getPortList() != null ? entry.getPortList() : "null"; + String firstSeen = dateTimeFormat.format( + entry.getFirstSeenMillis()); bw.write("r " + nickname + " " + fingerprint + " " + addressesBuilder.toString() + " " + validAfter + " " + orPort + " " + dirPort + " " + flagsBuilder.toString() + " " + consensusWeight + " " + countryCode + " " + hostName + " " + String.valueOf(lastRdnsLookup) + " " + defaultPolicy + " " - + portList + "\n"); + + portList + " " + firstSeen + "\n"); } for (Node entry : this.currentBridges.values()) { String nickname = entry.getNickname(); @@ -218,10 +225,12 @@ public class CurrentNodes { for (String relayFlag : entry.getRelayFlags()) { flagsBuilder.append((written++ > 0 ? "," : "") + relayFlag); } + String firstSeen = dateTimeFormat.format( + entry.getFirstSeenMillis()); bw.write("b " + nickname + " " + fingerprint + " " + addressesBuilder.toString() + " " + published + " " + orPort + " " + dirPort + " " + flagsBuilder.toString() - + " -1 ?? null -1 null null\n"); + + " -1 ?? null -1 null null " + firstSeen + "\n"); } bw.close(); } catch (IOException e) { @@ -294,7 +303,8 @@ public class CurrentNodes { String portList = entry.getPortList(); this.addRelay(nickname, fingerprint, address, orAddressesAndPorts, null, validAfterMillis, orPort, dirPort, relayFlags, - consensusWeight, null, null, -1L, defaultPolicy, portList); + consensusWeight, null, null, -1L, defaultPolicy, portList, + validAfterMillis); } if (this.lastValidAfterMillis == validAfterMillis) { this.lastBandwidthWeights = consensus.getBandwidthWeights(); @@ -306,7 +316,7 @@ public class CurrentNodes { SortedSet<String> exitAddresses, long validAfterMillis, int orPort, int dirPort, SortedSet<String> relayFlags, long consensusWeight, String countryCode, String hostName, long lastRdnsLookup, - String defaultPolicy, String portList) { + String defaultPolicy, String portList, long firstSeenMillis) { if (validAfterMillis >= cutoff && (!this.currentRelays.containsKey(fingerprint) || this.currentRelays.get(fingerprint).getLastSeenMillis() < @@ -318,10 +328,14 @@ public class CurrentNodes { hostName = previousRelay.getHostName(); lastRdnsLookup = previousRelay.getLastRdnsLookup(); } + if (previousRelay != null) { + firstSeenMillis = Math.min(firstSeenMillis, + previousRelay.getFirstSeenMillis()); + } Node entry = new Node(nickname, fingerprint, address, orAddressesAndPorts, exitAddresses, validAfterMillis, orPort, dirPort, relayFlags, consensusWeight, countryCode, hostName, - lastRdnsLookup, defaultPolicy, portList); + lastRdnsLookup, defaultPolicy, portList, firstSeenMillis); this.currentRelays.put(fingerprint, entry); if (validAfterMillis > this.lastValidAfterMillis) { this.lastValidAfterMillis = validAfterMillis; @@ -424,7 +438,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); + null, -1L, null, null, publishedMillis); } }
@@ -433,15 +447,21 @@ public class CurrentNodes { SortedSet<String> exitAddresses, long publishedMillis, int orPort, int dirPort, SortedSet<String> relayFlags, long consensusWeight, String countryCode, String hostname, long lastRdnsLookup, - String defaultPolicy, String portList) { + String defaultPolicy, String portList, long firstSeenMillis) { if (publishedMillis >= cutoff && (!this.currentBridges.containsKey(fingerprint) || this.currentBridges.get(fingerprint).getLastSeenMillis() < publishedMillis)) { + Node previousBridge = this.currentBridges.containsKey(fingerprint) + ? this.currentBridges.get(fingerprint) : null; + if (previousBridge != null) { + firstSeenMillis = Math.min(firstSeenMillis, + previousBridge.getFirstSeenMillis()); + } Node entry = new Node(nickname, fingerprint, address, orAddressesAndPorts, exitAddresses, publishedMillis, orPort, dirPort, relayFlags, consensusWeight, countryCode, hostname, - lastRdnsLookup, defaultPolicy, portList); + lastRdnsLookup, defaultPolicy, portList, firstSeenMillis); 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 bdc36e9..af0a0b9 100644 --- a/src/org/torproject/onionoo/DetailDataWriter.java +++ b/src/org/torproject/onionoo/DetailDataWriter.java @@ -572,6 +572,9 @@ public class DetailDataWriter { (addressesWritten++ > 0 ? "," : "") + """ + orAddress + """); } + String lastSeen = dateTimeFormat.format(entry.getLastSeenMillis()); + String firstSeen = dateTimeFormat.format( + entry.getFirstSeenMillis()); String running = entry.getRunning() ? "true" : "false"; int dirPort = entry.getDirPort(); String countryCode = entry.getCountryCode(); @@ -602,6 +605,8 @@ public class DetailDataWriter { sb.append(",\n"dir_address":"" + address + ":" + dirPort + """); } + sb.append(",\n"last_seen":"" + lastSeen + """); + sb.append(",\n"first_seen":"" + firstSeen + """); sb.append(",\n"running":" + running + ",\n"); SortedSet<String> relayFlags = entry.getRelayFlags(); if (!relayFlags.isEmpty()) { @@ -821,6 +826,9 @@ public class DetailDataWriter { /* Generate network-status-specific part. */ Node entry = bridge.getValue(); String nickname = entry.getNickname(); + String lastSeen = dateTimeFormat.format(entry.getLastSeenMillis()); + String firstSeen = dateTimeFormat.format( + entry.getFirstSeenMillis()); String running = entry.getRunning() ? "true" : "false"; String address = entry.getAddress(); SortedSet<String> orAddresses = new TreeSet<String>( @@ -838,8 +846,9 @@ public class DetailDataWriter { + ""nickname":"" + nickname + "",\n" + ""hashed_fingerprint":"" + fingerprint + "",\n" + ""or_addresses":[" + orAddressesAndPortsBuilder.toString() - + "],\n" - + ""running":" + running + ","); + + "],\n"last_seen":"" + lastSeen + "",\n"first_seen":"" + + firstSeen + "",\n"running":" + running + ","); + SortedSet<String> relayFlags = entry.getRelayFlags(); if (!relayFlags.isEmpty()) { sb.append("\n"flags":["); diff --git a/src/org/torproject/onionoo/Node.java b/src/org/torproject/onionoo/Node.java index 1bf984d..0857b4c 100644 --- a/src/org/torproject/onionoo/Node.java +++ b/src/org/torproject/onionoo/Node.java @@ -27,6 +27,7 @@ public class Node { private String cityName; private String aSName; private String aSNumber; + private long firstSeenMillis; private long lastSeenMillis; private int orPort; private int dirPort; @@ -47,7 +48,7 @@ public class Node { SortedSet<String> exitAddresses, long lastSeenMillis, int orPort, int dirPort, SortedSet<String> relayFlags, long consensusWeight, String countryCode, String hostName, long lastRdnsLookup, - String defaultPolicy, String portList) { + String defaultPolicy, String portList, long firstSeenMillis) { this.nickname = nickname; this.fingerprint = fingerprint; try { @@ -80,6 +81,7 @@ public class Node { this.lastRdnsLookup = lastRdnsLookup; this.defaultPolicy = defaultPolicy; this.portList = portList; + this.firstSeenMillis = firstSeenMillis; } public String getFingerprint() { return this.fingerprint; @@ -171,6 +173,9 @@ public class Node { public String getASName() { return this.aSName; } + public long getFirstSeenMillis() { + return this.firstSeenMillis; + } public long getLastSeenMillis() { return this.lastSeenMillis; } diff --git a/web/index.html b/web/index.html index 95b3866..ae084f1 100755 --- a/web/index.html +++ b/web/index.html @@ -124,6 +124,20 @@ Omitted if array is empty.</li> accepts directory connections. Optional field. Omitted if the relay does not accept directory connections.</li> +<li><b><font color="blue">"last_seen":</font></b> UTC timestamp +(YYYY-MM-DD hh:mm:ss) when this relay was last seen in a network status +consensus. +Required field. +Added field on December 5, 2012.</li> +<li><b><font color="blue">"first_seen":</font></b> UTC timestamp +(YYYY-MM-DD hh:mm:ss) when this relay was first seen in a network status +consensus. +Note that this timestamp is reset when a relay drops out of the consensus +for more than 7 days, so that whenever that relay rejoins, it would seem +as if it joined for the first time; this is a known limitation of the +current Onionoo design. +Required field. +Added field on December 5, 2012.</li> <li><b>"running":</b> Boolean field saying whether this relay was listed as running in the last relay network status consensus. Required field.</li> @@ -310,6 +324,20 @@ Sanitized IP addresses always change on the 1st of every month at 00:00:00 UTC, regardless of the bridge actually changing its IP address. TCP ports are not sanitized. Required field.</li> +<li><b><font color="blue">"last_seen":</font></b> UTC timestamp +(YYYY-MM-DD hh:mm:ss) when this bridge was last seen in a bridge network +status. +Required field. +Added field on December 5, 2012.</li> +<li><b><font color="blue">"first_seen":</font></b> UTC timestamp +(YYYY-MM-DD hh:mm:ss) when this bridge was first seen in a bridge network +status. +Note that this timestamp is reset when a bridge drops out of the network +status for more than 7 days, so that whenever that bridge rejoins, it +would seem as if it joined for the first time; this is a known limitation +of the current Onionoo design. +Required field. +Added field on December 5, 2012.</li> <li><b>"running":</b> Boolean field saying whether this bridge was listed as running in the last bridge network status. Required field.</li>
tor-commits@lists.torproject.org