[tor-commits] [onionoo/master] Add "[last|first]_seen" fields to details docs.

karsten at torproject.org karsten at torproject.org
Wed Dec 5 13:20:39 UTC 2012


commit 40a2faee73ee002c90582e16008b2211c2d1bb8f
Author: Karsten Loesing <karsten.loesing at 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>



More information about the tor-commits mailing list