public RelayCookie resolve(String serverID, String id) throws IOException {
    if (!Util.isQuickConnectId(serverID)) {
      throw new IllegalArgumentException("serverID isn't a Quick Connect ID");
    }

    RelayManager relayManager = (RelayManager) RelayHandler.getDefault();
    RelayCookie cookie = relayManager.get(serverID);
    if (cookie == null) {
      cookie = new RelayCookie.Builder().serverID(serverID).id(id).build();
      relayManager.put(serverID, cookie);
    }

    HttpUrl serverUrl = HttpUrl.parse("http://global.quickconnect.to/Serv.php");
    ServerInfoJson infoJson = getServerInfo(serverUrl, serverID, id);

    // ping DSM directly
    HttpUrl resolvedUrl = pingDSM(infoJson);
    if (resolvedUrl != null) {
      cookie = cookie.newBuilder().resolvedUrl(resolvedUrl).build();
      return cookie;
    }

    // ping DSM through tunnel
    resolvedUrl = pingTunnel(infoJson.service);
    if (resolvedUrl != null) {
      cookie = cookie.newBuilder().resolvedUrl(resolvedUrl).build();
      return cookie;
    }

    // request tunnel
    infoJson = requestTunnel(infoJson, serverID, id);
    if (infoJson != null) {
      resolvedUrl =
          requestUrl
              .newBuilder()
              .host(infoJson.service.relay_ip)
              .port(infoJson.service.relay_port)
              .build();
      cookie = cookie.newBuilder().resolvedUrl(resolvedUrl).build();
      return cookie;
    }

    throw new IOException("No valid url resolved");
  }