protected void closeTunnel(PairManagerTunnel tunnel) {

    System.out.println("Destroyed pair manager tunnel: " + tunnel.getString());

    synchronized (tunnels) {
      tunnels.remove(tunnel.getKey());
    }
  }
  protected void generateEvidence(IndentWriter writer) {
    writer.println("Tunnel Handler");

    writer.indent();

    writer.println("started=" + started + ", active=" + active);

    if (init_fail != null) {

      writer.println("Init fail: " + init_fail);
    }

    long now = SystemTime.getMonotonousTime();

    writer.println("total local=" + total_local_servers);
    writer.println(
        "last local create="
            + (last_local_server_create_time == 0
                ? "<never>"
                : String.valueOf(now - last_local_server_create_time)));
    writer.println(
        "last local agree="
            + (last_local_server_agree_time == 0
                ? "<never>"
                : String.valueOf(now - last_local_server_agree_time)));

    writer.println("total remote=" + total_servers);
    writer.println(
        "last remote create="
            + (last_server_create_time == 0
                ? "<never>"
                : String.valueOf(now - last_server_create_time)));
    writer.println(
        "last remote agree="
            + (last_server_agree_time == 0
                ? "<never>"
                : String.valueOf(now - last_server_agree_time)));

    synchronized (tunnels) {
      writer.println("tunnels=" + tunnels.size());

      for (PairManagerTunnel tunnel : tunnels.values()) {

        writer.println("    " + tunnel.getString());
      }
    }

    try {
      writer.println("IPv4 punchers: " + nat_punchers_ipv4.size());

      for (DHTNATPuncher p : nat_punchers_ipv4) {

        writer.println("    " + p.getStats());
      }

      writer.println("IPv6 punchers: " + nat_punchers_ipv6.size());

      for (DHTNATPuncher p : nat_punchers_ipv6) {

        writer.println("    " + p.getStats());
      }
    } finally {

      writer.exdent();
    }
  }
  private boolean createTunnel(
      InetAddress originator,
      long session,
      String sid,
      SecretKeySpec secret,
      String tunnel_url,
      String endpoint_url) {
    PairedServiceImpl ps = manager.getService(sid);

    if (ps == null) {

      Debug.out("Service '" + sid + "' not registered");

      return (false);
    }

    PairedServiceRequestHandler handler = ps.getHandler();

    if (handler == null) {

      Debug.out("Service '" + sid + "' has no handler registered");

      return (false);
    }

    String key = originator.getHostAddress() + ":" + session + ":" + sid;

    synchronized (tunnels) {
      PairManagerTunnel existing = tunnels.get(key);

      if (existing != null) {

        return (true);
      }

      if (tunnels.size() > MAX_TUNNELS) {

        long oldest_active = Long.MAX_VALUE;
        PairManagerTunnel oldest_tunnel = null;

        for (PairManagerTunnel t : tunnels.values()) {

          long at = t.getLastActive();

          if (at < oldest_active) {

            oldest_active = at;
            oldest_tunnel = t;
          }
        }

        oldest_tunnel.destroy();

        tunnels.remove(oldest_tunnel.getKey());
      }

      PairManagerTunnel tunnel =
          new PairManagerTunnel(
              this, key, originator, sid, handler, secret, tunnel_url, endpoint_url);

      tunnels.put(key, tunnel);

      System.out.println("Created pair manager tunnel: " + tunnel.getString());
    }

    return (true);
  }