private HttpUrl pingTunnel(ServiceJson serviceJson) {
    if (serviceJson == null || Util.isEmpty(serviceJson.relay_ip) || serviceJson.relay_port == 0) {
      return null;
    }

    // set timeout to 10 seconds
    OkHttpClient client =
        defaultClient
            .newBuilder()
            .connectTimeout(5, TimeUnit.SECONDS)
            .readTimeout(5, TimeUnit.SECONDS)
            .build();

    String relayIp = serviceJson.relay_ip;
    int relayPort = serviceJson.relay_port;

    // tunnel address
    ExecutorService executor = Executors.newFixedThreadPool(10);
    CompletionService<String> service = new ExecutorCompletionService<>(executor);
    service.submit(createPingTask(client, relayIp, relayPort));

    try {
      Future<String> future = service.take();
      if (future != null) {
        String host = future.get();
        if (!Util.isEmpty(host)) {
          return requestUrl.newBuilder().host(host).port(relayPort).build();
        }
      }
    } catch (InterruptedException | ExecutionException ignored) {
    }

    // shutdown executors
    executor.shutdownNow();

    return null;
  }
  public HttpUrl pingDSM(ServerInfoJson infoJson) {
    // set timeout to 5 seconds
    final OkHttpClient client =
        defaultClient
            .newBuilder()
            .connectTimeout(5, TimeUnit.SECONDS)
            .readTimeout(5, TimeUnit.SECONDS)
            .build();

    ServerJson serverJson = infoJson.server;
    if (serverJson == null) {
      throw new IllegalArgumentException("serverJson == null");
    }
    ServiceJson serviceJson = infoJson.service;
    if (serviceJson == null) {
      throw new IllegalArgumentException("serviceJson == null");
    }
    int port = serviceJson.port;
    int externalPort = serviceJson.ext_port;

    // internal address(192.168.x.x/10.x.x.x)
    ExecutorService executor = Executors.newFixedThreadPool(10);
    CompletionService<String> internalService = new ExecutorCompletionService<>(executor);
    List<InterfaceJson> ifaces = serverJson._interface;
    AtomicInteger internalCount = new AtomicInteger(0);
    if (ifaces != null) {
      for (final InterfaceJson iface : ifaces) {
        internalService.submit(createPingTask(client, iface.ip, port));
        internalCount.incrementAndGet();

        if (iface.ipv6 != null) {
          for (Ipv6Json ipv6 : iface.ipv6) {
            String ipv6Address = "[" + ipv6.address + "]";
            internalService.submit(createPingTask(client, ipv6Address, port));
            internalCount.incrementAndGet();
          }
        }
      }
    }

    // host address(ddns/fqdn)
    ExecutorCompletionService<String> hostService = new ExecutorCompletionService<>(executor);
    AtomicInteger hostCount = new AtomicInteger(0);
    // ddns
    if (!Util.isEmpty(serverJson.ddns) && !serverJson.ddns.equals("NULL")) {
      hostService.submit(createPingTask(client, serverJson.ddns, port));
      hostCount.incrementAndGet();
    }
    // fqdn
    if (!Util.isEmpty(serverJson.fqdn) && !serverJson.fqdn.equals("NULL")) {
      hostService.submit(createPingTask(client, serverJson.fqdn, port));
      hostCount.incrementAndGet();
    }

    // external address(public ip address)
    ExecutorCompletionService<String> externalService = new ExecutorCompletionService<>(executor);
    AtomicInteger externalCount = new AtomicInteger(0);
    if (serverJson.external != null) {
      String ip = serverJson.external.ip;
      if (!Util.isEmpty(ip)) {
        externalService.submit(
            createPingTask(client, ip, (externalPort != 0) ? externalPort : port));
        externalCount.incrementAndGet();
      }
      String ipv6 = serverJson.external.ipv6;
      if (!Util.isEmpty(ipv6) && !ipv6.equals("::")) {
        externalService.submit(
            createPingTask(client, "[" + ipv6 + "]", (externalPort != 0) ? externalPort : port));
        externalCount.incrementAndGet();
      }
    }

    while (internalCount.getAndDecrement() > 0) {
      try {
        Future<String> future = internalService.take();
        if (future != null) {
          String host = future.get();
          if (!Util.isEmpty(host)) {
            return requestUrl.newBuilder().host(host).port(port).build();
          }
        }
      } catch (InterruptedException | ExecutionException ignored) {
      }
    }

    while (hostCount.getAndDecrement() > 0) {
      try {
        Future<String> future = hostService.take();
        if (future != null) {
          String host = future.get();
          if (!Util.isEmpty(host)) {
            return requestUrl.newBuilder().host(host).port(port).build();
          }
        }
      } catch (InterruptedException | ExecutionException ignored) {
      }
    }

    while (externalCount.getAndDecrement() > 0) {
      try {
        Future<String> future = externalService.take();
        if (future != null) {
          String host = future.get();
          if (!Util.isEmpty(host)) {
            return requestUrl.newBuilder().host(host).port(port).build();
          }
        }
      } catch (InterruptedException | ExecutionException ignored) {
        //				ignored.printStackTrace();
      }
    }

    // shutdown executors
    executor.shutdownNow();

    return null;
  }