public void onGeneratedURI(FreenetURI uri, BaseClientPutter state, ObjectContainer container) {
   if (logMINOR) Logger.minor(this, "Generated URI for " + darknetOpennetString + " ARK: " + uri);
   long l = uri.getSuggestedEdition();
   if (l < crypto.myARKNumber) {
     Logger.error(
         this,
         "Inserted "
             + darknetOpennetString
             + " ARK edition # lower than attempted: "
             + l
             + " expected "
             + crypto.myARKNumber);
   } else if (l > crypto.myARKNumber) {
     if (logMINOR)
       Logger.minor(
           this,
           darknetOpennetString + " ARK number moving from " + crypto.myARKNumber + " to " + l);
     crypto.myARKNumber = l;
     if (crypto.isOpennet) node.writeOpennetFile();
     else node.writeNodeFile();
     // We'll broadcast the new ARK edition to our connected peers via a differential node
     // reference
     SimpleFieldSet fs = new SimpleFieldSet(true);
     fs.putSingle("ark.number", Long.toString(crypto.myARKNumber));
     node.peers.locallyBroadcastDiffNodeRef(fs, !crypto.isOpennet, crypto.isOpennet);
   }
 }
 /**
  * Combine the NodeIPDetector's output with any per-port information we may have to get a
  * definitive (for that port/NodeCrypto) list of IP addresses (still without port numbers).
  */
 FreenetInetAddress[] detectPrimaryIPAddress() {
   FreenetInetAddress addr = crypto.getBindTo();
   if (addr.isRealInternetAddress(false, true, false)) {
     // Binding to a real internet address => don't want us to use the others, most likely
     // he is on a multi-homed box where only one IP can be used for Freenet.
     return new FreenetInetAddress[] {addr};
   }
   return ipDetector.detectPrimaryIPAddress(!crypto.config.includeLocalAddressesInNoderefs);
 }
  public void update() {
    logMINOR = Logger.shouldLog(Logger.MINOR, this);
    if (logMINOR) Logger.minor(this, "update()");
    if (!checkIPUpdated()) return;
    // We'll broadcast the new physical.udp entry to our connected peers via a differential node
    // reference
    // We'll err on the side of caution and not update our peer to an empty physical.udp entry using
    // a differential node reference
    SimpleFieldSet nfs = crypto.exportPublicFieldSet(false, false, true);
    String[] entries = nfs.getAll("physical.udp");
    if (entries != null) {
      SimpleFieldSet fs = new SimpleFieldSet(true);
      fs.putOverwrite("physical.udp", entries);
      if (logMINOR)
        Logger.minor(this, darknetOpennetString + " ref's physical.udp is '" + fs.toString() + "'");
      node.peers.locallyBroadcastDiffNodeRef(fs, !crypto.isOpennet, crypto.isOpennet);
    } else {
      if (logMINOR) Logger.minor(this, darknetOpennetString + " ref's physical.udp is null");
    }
    // Proceed with inserting the ARK
    if (logMINOR)
      Logger.minor(this, "Inserting " + darknetOpennetString + " ARK because peers list changed");

    if (inserter != null) {
      // Already inserting.
      // Re-insert after finished.
      synchronized (this) {
        shouldInsert = true;
      }

      return;
    }
    // Otherwise need to start an insert
    if (node.noConnectedPeers()) {
      // Can't start an insert yet
      synchronized (this) {
        shouldInsert = true;
      }
      return;
    }

    startInserter();
  }
  private void startInserter() {
    if (!canStart) {
      if (logMINOR) Logger.minor(this, darknetOpennetString + " ARK inserter can't start yet");
      return;
    }

    if (logMINOR) Logger.minor(this, "starting " + darknetOpennetString + " ARK inserter");

    SimpleFieldSet fs = crypto.exportPublicFieldSet(false, false, true);

    // Remove some unnecessary fields that only cause collisions.

    // Delete entire ark.* field for now. Changing this and automatically moving to the new may be
    // supported in future.
    fs.removeSubset("ark");
    fs.removeValue("location");
    fs.removeValue("sig");
    // fs.remove("version"); - keep version because of its significance in reconnection

    String s = fs.toString();

    byte[] buf;
    try {
      buf = s.getBytes("UTF-8");
    } catch (UnsupportedEncodingException e) {
      throw new Error("Impossible: JVM doesn't support UTF-8: " + e, e);
    }

    Bucket b = new SimpleReadOnlyArrayBucket(buf);

    long number = crypto.myARKNumber;
    InsertableClientSSK ark = crypto.myARK;
    FreenetURI uri = ark.getInsertURI().setKeyType("USK").setSuggestedEdition(number);

    if (logMINOR)
      Logger.minor(
          this, "Inserting " + darknetOpennetString + " ARK: " + uri + "  contents:\n" + s);

    inserter =
        new ClientPutter(
            this,
            b,
            uri,
            new ClientMetadata("text/plain") /* it won't quite fit in an SSK anyway */,
            node.clientCore.makeClient((short) 0, true).getInsertContext(true),
            RequestStarter.INTERACTIVE_PRIORITY_CLASS,
            false,
            false,
            this,
            null,
            null,
            false);

    try {

      node.clientCore.clientContext.start(inserter, false);

      synchronized (this) {
        if (fs.get("physical.udp") == null) lastInsertedPeers = null;
        else {
          try {
            String[] all = fs.getAll("physical.udp");
            Peer[] peers = new Peer[all.length];
            for (int i = 0; i < all.length; i++) peers[i] = new Peer(all[i], false);
            lastInsertedPeers = peers;
          } catch (PeerParseException e1) {
            Logger.error(
                this,
                "Error parsing own "
                    + darknetOpennetString
                    + " ref: "
                    + e1
                    + " : "
                    + fs.get("physical.udp"),
                e1);
          } catch (UnknownHostException e1) {
            Logger.error(
                this,
                "Error parsing own "
                    + darknetOpennetString
                    + " ref: "
                    + e1
                    + " : "
                    + fs.get("physical.udp"),
                e1);
          }
        }
      }
    } catch (InsertException e) {
      onFailure(e, inserter, null);
    } catch (DatabaseDisabledException e) {
      // Impossible
    }
  }
  /**
   * Get our Peer's. This is a list of IP:port's at which we might be contactable. Some of them will
   * have the same port as the listenPort, but if we are behind a NAT which rewrites our port
   * number, some of them may not. (If we're behind a symmetric NAT which rewrites it differently
   * for each connection, we're stuffed, and we tell the user).
   */
  Peer[] detectPrimaryPeers() {
    final boolean logMINOR = NodeIPPortDetector.logMINOR;
    ArrayList<Peer> addresses = new ArrayList<Peer>();
    FreenetInetAddress[] addrs = detectPrimaryIPAddress();
    for (FreenetInetAddress addr : addrs) {
      addresses.add(new Peer(addr, crypto.portNumber));
      if (logMINOR) Logger.minor(this, "Adding " + addr);
    }
    // Now try to get the rewritten port number from our peers.
    // Only considering those within this crypto port, this time.

    PeerNode[] peerList = crypto.getPeerNodes();

    if (peerList != null) {
      HashMap<Peer, Integer> countsByPeer = new HashMap<Peer, Integer>();
      // FIXME use a standard mutable int object, we have one somewhere
      for (PeerNode pn : peerList) {
        Peer p = pn.getRemoteDetectedPeer();
        if ((p == null) || p.isNull()) continue;
        // DNSRequester doesn't deal with our own node
        if (!IPUtil.isValidAddress(p.getAddress(true), false)) continue;
        if (logMINOR) Logger.minor(this, "Peer " + pn.getPeer() + " thinks we are " + p);
        if (countsByPeer.containsKey(p)) {
          countsByPeer.put(p, countsByPeer.get(p) + 1);
        } else {
          countsByPeer.put(p, 1);
        }
      }
      if (countsByPeer.size() == 1) {
        Iterator<Peer> it = countsByPeer.keySet().iterator();
        Peer p = (it.next());
        Logger.minor(this, "Everyone agrees we are " + p);
        if (!addresses.contains(p)) {
          addresses.add(p);
        }
      } else if (countsByPeer.size() > 1) {
        // Take two most popular addresses.
        Peer best = null;
        Peer secondBest = null;
        int bestPopularity = 0;
        int secondBestPopularity = 0;
        for (Map.Entry<Peer, Integer> entry : countsByPeer.entrySet()) {
          Peer cur = entry.getKey();
          int curPop = entry.getValue();
          Logger.normal(this, "Detected peer: " + cur + " popularity " + curPop);
          if (curPop >= bestPopularity) {
            secondBestPopularity = bestPopularity;
            bestPopularity = curPop;
            secondBest = best;
            best = cur;
          }
        }
        if (best != null) {
          if ((bestPopularity > 1) || (addrs.length == 0)) {
            if (!addresses.contains(best)) {
              Logger.normal(this, "Adding best peer " + best + " (" + bestPopularity + ')');
              addresses.add(best);
            }
            if ((secondBest != null) && (secondBestPopularity > 1)) {
              if (!addresses.contains(secondBest)) {
                Logger.normal(
                    this, "Adding second best peer " + secondBest + " (" + secondBest + ')');
                addresses.add(secondBest);
              }
              if (best.getAddress().equals(secondBest.getAddress()) && bestPopularity == 1) {
                Logger.error(
                    this, "Hrrrm, maybe this is a symmetric NAT? Expect trouble connecting!");
                System.err.println(
                    "Hrrrm, maybe this is a symmetric NAT? Expect trouble connecting!");

                ipDetector.setMaybeSymmetric();

                Peer p = new Peer(best.getFreenetAddress(), crypto.portNumber);
                if (!addresses.contains(p)) addresses.add(p);
              }
            }
          }
        }
      }
    }
    lastPeers = addresses.toArray(new Peer[addresses.size()]);
    if (logMINOR)
      Logger.minor(
          this, "Returning for port " + crypto.portNumber + " : " + Arrays.toString(lastPeers));
    return lastPeers;
  }