/* Tests a tcp server with multiple ports. TODO(daniel-j-born): Multiple fds for
   the same port should be tested. */
static void test_connect(unsigned n) {
    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
    struct sockaddr_storage addr;
    struct sockaddr_storage addr1;
    socklen_t addr_len = sizeof(addr);
    unsigned svr_fd_count;
    int svr_port;
    unsigned svr1_fd_count;
    int svr1_port;
    grpc_tcp_server *s = grpc_tcp_server_create(NULL);
    unsigned i;
    server_weak_ref weak_ref;
    server_weak_ref_init(&weak_ref);
    LOG_TEST("test_connect");
    gpr_log(GPR_INFO, "clients=%d", n);
    memset(&addr, 0, sizeof(addr));
    memset(&addr1, 0, sizeof(addr1));
    addr.ss_family = addr1.ss_family = AF_INET;
    svr_port = grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, addr_len);
    GPR_ASSERT(svr_port > 0);
    /* Cannot use wildcard (port==0), because add_port() will try to reuse the
       same port as a previous add_port(). */
    svr1_port = grpc_pick_unused_port_or_die();
    grpc_sockaddr_set_port((struct sockaddr *)&addr1, svr1_port);
    GPR_ASSERT(grpc_tcp_server_add_port(s, (struct sockaddr *)&addr1, addr_len) ==
               svr1_port);

    /* Bad port_index. */
    GPR_ASSERT(grpc_tcp_server_port_fd_count(s, 2) == 0);
    GPR_ASSERT(grpc_tcp_server_port_fd(s, 2, 0) < 0);

    /* Bad fd_index. */
    GPR_ASSERT(grpc_tcp_server_port_fd(s, 0, 100) < 0);
    GPR_ASSERT(grpc_tcp_server_port_fd(s, 1, 100) < 0);

    /* Got at least one fd per port. */
    svr_fd_count = grpc_tcp_server_port_fd_count(s, 0);
    GPR_ASSERT(svr_fd_count >= 1);
    svr1_fd_count = grpc_tcp_server_port_fd_count(s, 1);
    GPR_ASSERT(svr1_fd_count >= 1);

    for (i = 0; i < svr_fd_count; ++i) {
        int fd = grpc_tcp_server_port_fd(s, 0, i);
        GPR_ASSERT(fd >= 0);
        if (i == 0) {
            GPR_ASSERT(getsockname(fd, (struct sockaddr *)&addr, &addr_len) == 0);
            GPR_ASSERT(addr_len <= sizeof(addr));
        }
    }
    for (i = 0; i < svr1_fd_count; ++i) {
        int fd = grpc_tcp_server_port_fd(s, 1, i);
        GPR_ASSERT(fd >= 0);
        if (i == 0) {
            GPR_ASSERT(getsockname(fd, (struct sockaddr *)&addr1, &addr_len) == 0);
            GPR_ASSERT(addr_len <= sizeof(addr1));
        }
    }

    grpc_tcp_server_start(&exec_ctx, s, &g_pollset, 1, on_connect, NULL);

    for (i = 0; i < n; i++) {
        on_connect_result result;
        int svr_fd;
        on_connect_result_init(&result);
        tcp_connect(&exec_ctx, (struct sockaddr *)&addr, addr_len, &result);
        GPR_ASSERT(result.server_fd >= 0);
        svr_fd = result.server_fd;
        GPR_ASSERT(grpc_tcp_server_port_fd(s, result.port_index, result.fd_index) ==
                   result.server_fd);
        GPR_ASSERT(result.port_index == 0);
        GPR_ASSERT(result.fd_index < svr_fd_count);
        GPR_ASSERT(result.server == s);
        if (weak_ref.server == NULL) {
            server_weak_ref_set(&weak_ref, result.server);
        }
        grpc_tcp_server_unref(&exec_ctx, result.server);

        on_connect_result_init(&result);
        tcp_connect(&exec_ctx, (struct sockaddr *)&addr1, addr_len, &result);
        GPR_ASSERT(result.server_fd >= 0);
        GPR_ASSERT(result.server_fd != svr_fd);
        GPR_ASSERT(grpc_tcp_server_port_fd(s, result.port_index, result.fd_index) ==
                   result.server_fd);
        GPR_ASSERT(result.port_index == 1);
        GPR_ASSERT(result.fd_index < svr_fd_count);
        GPR_ASSERT(result.server == s);
        grpc_tcp_server_unref(&exec_ctx, result.server);
    }

    /* Weak ref to server valid until final unref. */
    GPR_ASSERT(weak_ref.server != NULL);
    GPR_ASSERT(grpc_tcp_server_port_fd(s, 0, 0) >= 0);

    grpc_tcp_server_unref(&exec_ctx, s);

    /* Weak ref lost. */
    GPR_ASSERT(weak_ref.server == NULL);

    grpc_exec_ctx_finish(&exec_ctx);
}
/* Tests a tcp server on "::" listeners with multiple ports. If channel_args is
   non-NULL, pass them to the server. If dst_addrs is non-NULL, use valid addrs
   as destination addrs (port is not set). If dst_addrs is NULL, use listener
   addrs as destination addrs. If test_dst_addrs is true, test connectivity with
   each destination address, set grpc_resolved_address::len=0 for failures, but
   don't fail the overall unitest. */
static void test_connect(size_t num_connects,
                         const grpc_channel_args *channel_args,
                         test_addrs *dst_addrs, bool test_dst_addrs) {
  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
  grpc_resolved_address resolved_addr;
  grpc_resolved_address resolved_addr1;
  struct sockaddr_storage *const addr =
      (struct sockaddr_storage *)resolved_addr.addr;
  struct sockaddr_storage *const addr1 =
      (struct sockaddr_storage *)resolved_addr1.addr;
  unsigned svr_fd_count;
  int port;
  int svr_port;
  unsigned svr1_fd_count;
  int svr1_port;
  grpc_tcp_server *s;
  const unsigned num_ports = 2;
  GPR_ASSERT(GRPC_ERROR_NONE ==
             grpc_tcp_server_create(&exec_ctx, NULL, channel_args, &s));
  unsigned port_num;
  server_weak_ref weak_ref;
  server_weak_ref_init(&weak_ref);
  server_weak_ref_set(&weak_ref, s);
  LOG_TEST("test_connect");
  gpr_log(GPR_INFO,
          "clients=%lu, num chan args=%lu, remote IP=%s, test_dst_addrs=%d",
          (unsigned long)num_connects,
          (unsigned long)(channel_args != NULL ? channel_args->num_args : 0),
          dst_addrs != NULL ? "<specific>" : "::", test_dst_addrs);
  memset(&resolved_addr, 0, sizeof(resolved_addr));
  memset(&resolved_addr1, 0, sizeof(resolved_addr1));
  resolved_addr.len = sizeof(struct sockaddr_storage);
  resolved_addr1.len = sizeof(struct sockaddr_storage);
  addr->ss_family = addr1->ss_family = AF_INET;
  GPR_ASSERT(GRPC_LOG_IF_ERROR(
      "grpc_tcp_server_add_port",
      grpc_tcp_server_add_port(s, &resolved_addr, &svr_port)));
  gpr_log(GPR_INFO, "Allocated port %d", svr_port);
  GPR_ASSERT(svr_port > 0);
  /* Cannot use wildcard (port==0), because add_port() will try to reuse the
     same port as a previous add_port(). */
  svr1_port = grpc_pick_unused_port_or_die();
  GPR_ASSERT(svr1_port > 0);
  gpr_log(GPR_INFO, "Picked unused port %d", svr1_port);
  grpc_sockaddr_set_port(&resolved_addr1, svr1_port);
  GPR_ASSERT(grpc_tcp_server_add_port(s, &resolved_addr1, &port) ==
                 GRPC_ERROR_NONE &&
             port == svr1_port);

  /* Bad port_index. */
  GPR_ASSERT(grpc_tcp_server_port_fd_count(s, 2) == 0);
  GPR_ASSERT(grpc_tcp_server_port_fd(s, 2, 0) < 0);

  /* Bad fd_index. */
  GPR_ASSERT(grpc_tcp_server_port_fd(s, 0, 100) < 0);
  GPR_ASSERT(grpc_tcp_server_port_fd(s, 1, 100) < 0);

  /* Got at least one fd per port. */
  svr_fd_count = grpc_tcp_server_port_fd_count(s, 0);
  GPR_ASSERT(svr_fd_count >= 1);
  svr1_fd_count = grpc_tcp_server_port_fd_count(s, 1);
  GPR_ASSERT(svr1_fd_count >= 1);

  grpc_tcp_server_start(&exec_ctx, s, &g_pollset, 1, on_connect, NULL);

  if (dst_addrs != NULL) {
    int ports[] = {svr_port, svr1_port};
    for (port_num = 0; port_num < num_ports; ++port_num) {
      size_t dst_idx;
      size_t num_tested = 0;
      for (dst_idx = 0; dst_idx < dst_addrs->naddrs; ++dst_idx) {
        test_addr dst = dst_addrs->addrs[dst_idx];
        on_connect_result result;
        grpc_error *err;
        if (dst.addr.len == 0) {
          gpr_log(GPR_DEBUG, "Skipping test of non-functional local IP %s",
                  dst.str);
          continue;
        }
        GPR_ASSERT(grpc_sockaddr_set_port(&dst.addr, ports[port_num]));
        test_addr_init_str(&dst);
        ++num_tested;
        on_connect_result_init(&result);
        if ((err = tcp_connect(&exec_ctx, &dst, &result)) == GRPC_ERROR_NONE &&
            result.server_fd >= 0 && result.server == s) {
          continue;
        }
        gpr_log(GPR_ERROR, "Failed to connect to %s: %s", dst.str,
                grpc_error_string(err));
        GPR_ASSERT(test_dst_addrs);
        dst_addrs->addrs[dst_idx].addr.len = 0;
        GRPC_ERROR_UNREF(err);
      }
      GPR_ASSERT(num_tested > 0);
    }
  } else {
    for (port_num = 0; port_num < num_ports; ++port_num) {
      const unsigned num_fds = grpc_tcp_server_port_fd_count(s, port_num);
      unsigned fd_num;
      for (fd_num = 0; fd_num < num_fds; ++fd_num) {
        int fd = grpc_tcp_server_port_fd(s, port_num, fd_num);
        size_t connect_num;
        test_addr dst;
        GPR_ASSERT(fd >= 0);
        dst.addr.len = sizeof(dst.addr.addr);
        GPR_ASSERT(getsockname(fd, (struct sockaddr *)dst.addr.addr,
                               (socklen_t *)&dst.addr.len) == 0);
        GPR_ASSERT(dst.addr.len <= sizeof(dst.addr.addr));
        test_addr_init_str(&dst);
        gpr_log(GPR_INFO, "(%d, %d) fd %d family %s listening on %s", port_num,
                fd_num, fd, sock_family_name(addr->ss_family), dst.str);
        for (connect_num = 0; connect_num < num_connects; ++connect_num) {
          on_connect_result result;
          on_connect_result_init(&result);
          GPR_ASSERT(GRPC_LOG_IF_ERROR("tcp_connect",
                                       tcp_connect(&exec_ctx, &dst, &result)));
          GPR_ASSERT(result.server_fd == fd);
          GPR_ASSERT(result.port_index == port_num);
          GPR_ASSERT(result.fd_index == fd_num);
          GPR_ASSERT(result.server == s);
          GPR_ASSERT(
              grpc_tcp_server_port_fd(s, result.port_index, result.fd_index) ==
              result.server_fd);
        }
      }
    }
  }
  /* Weak ref to server valid until final unref. */
  GPR_ASSERT(weak_ref.server != NULL);
  GPR_ASSERT(grpc_tcp_server_port_fd(s, 0, 0) >= 0);

  grpc_tcp_server_unref(&exec_ctx, s);
  grpc_exec_ctx_finish(&exec_ctx);

  /* Weak ref lost. */
  GPR_ASSERT(weak_ref.server == NULL);
}