Example #1
0
END_TEST

START_TEST (inet_connect_ipv6_test) {
#ifdef PR_USE_IPV6
  int res;
  conn_t *conn;
  const pr_netaddr_t *addr;
  unsigned char use_ipv6;

  use_ipv6 = pr_netaddr_use_ipv6();
  pr_netaddr_enable_ipv6();
  pr_inet_set_default_family(p, AF_INET6);

  conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE);
  fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno));

  addr = pr_netaddr_get_addr(p, "::1", NULL);
  fail_unless(addr != NULL, "Failed to resolve '::1': %s", strerror(errno));

  mark_point();
  res = proxy_inet_connect(p, conn, addr, 80);
  fail_unless(res < 0, "Connected to 127.0.0.1#80 unexpectedly");
  fail_unless(errno == ECONNREFUSED || errno == ENETUNREACH,
    "Expected ECONNREFUSED (%d) or ENETUNREACH (%d), got %s (%d)",
    ECONNREFUSED, ENETUNREACH, strerror(errno), errno);
  proxy_inet_close(p, conn);

  /* Try connecting to Google's DNS server. */

  conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE);
  fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno));

  addr = pr_netaddr_get_addr(p, "2001:4860:4860::8888", NULL);
  fail_unless(addr != NULL, "Failed to resolve '2001:4860:4860::8888': %s",
    strerror(errno));

  mark_point();
  res = proxy_inet_connect(p, conn, addr, 53);
  if (res < 0) {
    /* This could be expected, e.g. if there's no route. */
    fail_unless(errno == EHOSTUNREACH || errno == ENETUNREACH,
      "Expected EHOSTUNREACH (%d) or ENETUNREACH (%d), got %s (%d)",
      EHOSTUNREACH, ENETUNREACH, strerror(errno), errno);
  }

  mark_point();
  proxy_inet_close(p, conn);

  pr_inet_set_default_family(p, AF_INET);

  if (use_ipv6 == FALSE) {
    pr_netaddr_disable_ipv6();
  }
#endif /* PR_USE_IPV6 */
}
Example #2
0
END_TEST

START_TEST (inet_close_test) {
  conn_t *conn;

  mark_point();
  proxy_inet_close(NULL, NULL);

  conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE);
  fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno));

  mark_point();
  conn->rfd = conn->wfd = 999;
  proxy_inet_close(NULL, conn);
}
Example #3
0
END_TEST

START_TEST (inet_openrw_test) {
  conn_t *res, *conn;
  const pr_netaddr_t *addr;

  res = proxy_inet_openrw(NULL, NULL, NULL, PR_NETIO_STRM_CTRL, -1, -1, -1,
    FALSE);
  fail_unless(res == NULL, "Failed to handle null arguments");
  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
    strerror(errno), errno);

  res = proxy_inet_openrw(p, NULL, NULL, PR_NETIO_STRM_CTRL, -1, -1, -1,
    FALSE);
  fail_unless(res == NULL, "Failed to handle null conn");
  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
    strerror(errno), errno);

  conn = pr_inet_create_conn(p, -2, NULL, INPORT_ANY, FALSE);
  fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno));

  res = proxy_inet_openrw(p, conn, NULL, PR_NETIO_STRM_OTHR, -1, -1, -1,
    FALSE);
  fail_unless(res == NULL, "Failed to handle null addr");
  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
    strerror(errno), errno);
  proxy_inet_close(p, conn);

  addr = pr_netaddr_get_addr(p, "127.0.0.1", NULL);
  fail_unless(addr != NULL, "Failed to resolve '127.0.0.1': %s",
    strerror(errno));

  res = proxy_inet_openrw(p, conn, addr, PR_NETIO_STRM_OTHR, -1, -1, -1,
    FALSE);
  fail_unless(res != NULL, "Failed to open rw conn: %s", strerror(errno));
  proxy_inet_close(p, conn);
}
Example #4
0
END_TEST

START_TEST (inet_connect_ipv4_test) {
  int res;
  conn_t *conn;
  const pr_netaddr_t *addr;

  mark_point();
  res = proxy_inet_connect(NULL, NULL, NULL, 0);
  fail_unless(res < 0, "Failed to handle null pool");
  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
    strerror(errno), errno);

  mark_point();
  res = proxy_inet_connect(p, NULL, NULL, 0);
  fail_unless(res < 0, "Failed to handle null conn");
  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
    strerror(errno), errno);

  conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE);
  fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno));

  mark_point();
  res = proxy_inet_connect(p, conn, NULL, 0);
  fail_unless(res < 0, "Failed to handle null addr");
  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
    strerror(errno), errno);

  addr = pr_netaddr_get_addr(p, "127.0.0.1", NULL);
  fail_unless(addr != NULL, "Failed to resolve '127.0.0.1': %s",
    strerror(errno));

  mark_point();
  res = proxy_inet_connect(p, conn, addr, 80);
  fail_unless(res < 0, "Connected to 127.0.0.1#80 unexpectedly");
  fail_unless(errno == ECONNREFUSED,
    "Expected ECONNREFUSED (%d), got '%s' (%d)", ECONNREFUSED,
    strerror(errno), errno);
  proxy_inet_close(p, conn);

  /* Try connecting to Google's DNS server. */

  addr = pr_netaddr_get_addr(p, "8.8.8.8", NULL);
  fail_unless(addr != NULL, "Failed to resolve '8.8.8.8': %s",
    strerror(errno));

  conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE);
  fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno));

  mark_point();
  res = proxy_inet_connect(p, conn, addr, 53);
  fail_if(res < 0, "Failed to connect to 8.8.8.8#53: %s", strerror(errno));

  mark_point();
  proxy_inet_close(p, conn);

  /* Now start supplying in/out streams. */

  conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE);
  fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno));

  conn->instrm = pr_netio_open(p, PR_NETIO_STRM_CTRL, -1, PR_NETIO_IO_RD);
  fail_unless(conn->instrm != NULL, "Failed to open ctrl reading stream: %s",
    strerror(errno));

  mark_point();
  res = proxy_inet_connect(p, conn, addr, 53);
  fail_if(res < 0, "Failed to connect to 8.8.8.8#53: %s", strerror(errno));

  mark_point();
  proxy_inet_close(p, conn);

  conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE);
  fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno));

  conn->instrm = pr_netio_open(p, PR_NETIO_STRM_CTRL, -1, PR_NETIO_IO_RD);
  fail_unless(conn->instrm != NULL, "Failed to open ctrl reading stream: %s",
    strerror(errno));

  conn->outstrm = pr_netio_open(p, PR_NETIO_STRM_OTHR, -1, PR_NETIO_IO_WR);
  fail_unless(conn->outstrm != NULL, "Failed to open othr writing stream: %s",
    strerror(errno));

  mark_point();
  res = proxy_inet_connect(p, conn, addr, 53);
  fail_if(res < 0, "Failed to connect to 8.8.8.8#53: %s", strerror(errno));

  mark_point();
  proxy_inet_close(p, conn);

}
Example #5
0
END_TEST

START_TEST (inet_listen_test) {
  int res;
  conn_t *conn;

  mark_point();
  res = proxy_inet_listen(NULL, NULL, 0, 0);
  fail_unless(res < 0, "Failed to handle null arguments");
  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
    strerror(errno), errno);

  mark_point();
  res = proxy_inet_listen(p, NULL, 0, 0);
  fail_unless(res < 0, "Failed to handle null conn");
  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
    strerror(errno), errno);

  conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE);
  fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno));

  mark_point();
  res = proxy_inet_listen(p, conn, 5, 0);
  fail_unless(res == 0, "Failed to listen on conn: %s", strerror(errno));

  mark_point();
  proxy_inet_close(p, conn);

  /* Now start providing in/out streams. */

  conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE);
  fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno));

  conn->instrm = pr_netio_open(p, PR_NETIO_STRM_CTRL, -1, PR_NETIO_IO_RD);
  fail_unless(conn->instrm != NULL, "Failed to open ctrl reading stream: %s",
    strerror(errno));

  mark_point();
  res = proxy_inet_listen(p, conn, 5, 0);
  fail_unless(res == 0, "Failed to listen on conn: %s", strerror(errno));

  mark_point();
  proxy_inet_close(p, conn);

  conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE);
  fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno));

  conn->instrm = pr_netio_open(p, PR_NETIO_STRM_CTRL, -1, PR_NETIO_IO_RD);
  fail_unless(conn->instrm != NULL, "Failed to open ctrl reading stream: %s",
    strerror(errno));

  conn->outstrm = pr_netio_open(p, PR_NETIO_STRM_OTHR, -1, PR_NETIO_IO_WR);
  fail_unless(conn->outstrm != NULL, "Failed to open othr writing stream: %s",
    strerror(errno));

  mark_point();
  res = proxy_inet_listen(p, conn, 5, 0);
  fail_unless(res == 0, "Failed to listen on conn: %s", strerror(errno));

  mark_point();
  proxy_inet_close(p, conn);
}
Example #6
0
int proxy_ftp_xfer_prepare_active(int policy_id, cmd_rec *cmd,
    const char *error_code, struct proxy_session *proxy_sess, int flags) {
  int backend_family, bind_family, res, xerrno = 0;
  cmd_rec *actv_cmd;
  const pr_netaddr_t *bind_addr = NULL;
  pr_response_t *resp;
  unsigned int resp_nlines = 0;
  conn_t *data_conn = NULL;
  char *active_cmd;
  const char *resp_msg = NULL;

  if (cmd == NULL ||
      error_code == NULL ||
      proxy_sess == NULL ||
      proxy_sess->backend_ctrl_conn == NULL) {
    errno = EINVAL;
    return -1;
  }

  switch (policy_id) {
    case PR_CMD_PORT_ID:
      active_cmd = C_PORT;
      break;

    case PR_CMD_EPRT_ID:
      /* If the remote host does not mention EPRT in its features, fall back
       * to using PORT.
       */
      active_cmd = C_EPRT;
      if (pr_table_get(proxy_sess->backend_features, C_EPRT, NULL) == NULL) {
        pr_trace_msg(trace_channel, 19,
          "EPRT not supported by backend server (via FEAT), using PORT");
        if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) {
          proxy_sess->dataxfer_policy = PR_CMD_PORT_ID;
        }

        active_cmd = C_PORT;
        policy_id = PR_CMD_PORT_ID;
      }
      break;

    default:
      /* In this case, the cmd we were given is the one we should send to
       * the backend server -- but only if it is either EPRT or PORT.
       */
      if (pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) != 0 &&
          pr_cmd_cmp(cmd, PR_CMD_PORT_ID) != 0) {
        pr_trace_msg(trace_channel, 9,
          "illegal FTP active transfer command '%s'", (char *) cmd->argv[0]);
        errno = EINVAL;
        return -1;
      }

      active_cmd = cmd->argv[0];

      if (pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) == 0) {
        /* If the remote host does not mention EPRT in its features, fall back
         * to using PORT.
         */
        if (pr_table_get(proxy_sess->backend_features, C_EPRT, NULL) == NULL) {
          pr_trace_msg(trace_channel, 19,
            "EPRT not supported by backend server (via FEAT), using PORT");
          if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) {
            proxy_sess->dataxfer_policy = PR_CMD_PORT_ID;
          }

          active_cmd = C_PORT;
          policy_id = PR_CMD_PORT_ID;
        }
      }

      break;
  }

  bind_addr = proxy_sess->src_addr;
  if (bind_addr == NULL) {
    bind_addr = session.c->local_addr;
  }

  if (pr_netaddr_is_loopback(bind_addr) == TRUE &&
      pr_netaddr_is_loopback(proxy_sess->backend_ctrl_conn->remote_addr) != TRUE) {
    const char *local_name;
    const pr_netaddr_t *local_addr;

    local_name = pr_netaddr_get_localaddr_str(cmd->pool);
    local_addr = pr_netaddr_get_addr(cmd->pool, local_name, NULL);

    if (local_addr != NULL) {
      pr_trace_msg(trace_channel, 14,
        "%s is a loopback address, using %s instead",
        pr_netaddr_get_ipstr(bind_addr), pr_netaddr_get_ipstr(local_addr));
      bind_addr = local_addr;
    }
  }

  /* Need to check the family of the bind addr against the family of the
   * backend server.
   */
  backend_family = pr_netaddr_get_family(proxy_sess->backend_ctrl_conn->remote_addr);
  bind_family = pr_netaddr_get_family(bind_addr);

  if (bind_family == backend_family) {
#ifdef PR_USE_IPV6
    if (pr_netaddr_use_ipv6()) {
      /* Make sure that the family is NOT IPv6, even though the local and
       * remote families match.  The PORT command cannot be used for IPv6
       * addresses -- but EPRT CAN be used for IPv6 addresses.
       */
      if (bind_family == AF_INET6 &&
          policy_id == PR_CMD_PORT_ID) {
        pr_netaddr_t *mapped_addr;

        mapped_addr = pr_netaddr_v6tov4(cmd->pool, bind_addr);
        if (mapped_addr != NULL) {
          pr_trace_msg(trace_channel, 9,
            "converting local IPv6 address '%s' to IPv4 address '%s' for "
            "active transfer using PORT", pr_netaddr_get_ipstr(bind_addr),
            pr_netaddr_get_ipstr(mapped_addr));
          bind_addr = mapped_addr;
        }
      }
    }
#endif /* PR_USE_IPV6 */
  } else {
    if (backend_family == AF_INET) {
      pr_netaddr_t *mapped_addr;

      /* In this scenario, the remote peer is an IPv4 (or IPv4-mapped IPv6)
       * peer, so make sure we use an IPv4 local address.
       */
      mapped_addr = pr_netaddr_v6tov4(cmd->pool, bind_addr);
      if (mapped_addr != NULL) {
        pr_trace_msg(trace_channel, 9,
          "converting local IPv6 address '%s' to IPv4 address '%s' for "
          "active transfer with IPv4 peer", pr_netaddr_get_ipstr(bind_addr),
          pr_netaddr_get_ipstr(mapped_addr));
        bind_addr = mapped_addr;
      }
    }
  }

  if (proxy_sess->backend_data_conn != NULL) {
    /* Make sure that we only have one backend data connection. */
    proxy_inet_close(session.pool, proxy_sess->backend_data_conn);
    proxy_sess->backend_data_conn = NULL;
  }

  data_conn = proxy_ftp_conn_listen(cmd->tmp_pool, bind_addr, FALSE);
  if (data_conn == NULL) {
    xerrno = errno;

    pr_response_add_err(error_code,
      _("Unable to build data connection: Internal error"));
    pr_response_flush(&resp_err_list);

    errno = xerrno;
    return -1;
  }

  proxy_sess->backend_data_conn = data_conn;

  switch (pr_cmd_get_id(active_cmd)) {
    case PR_CMD_PORT_ID:
      resp_msg = proxy_ftp_msg_fmt_addr(cmd->tmp_pool, data_conn->local_addr,
        data_conn->local_port, FALSE);
      break;

    case PR_CMD_EPRT_ID:
      resp_msg = proxy_ftp_msg_fmt_ext_addr(cmd->tmp_pool,
        data_conn->local_addr, data_conn->local_port, PR_CMD_EPRT_ID, FALSE);
      break;
  }

  actv_cmd = pr_cmd_alloc(cmd->tmp_pool, 2, active_cmd, resp_msg);
  actv_cmd->arg = (char *) resp_msg;

  pr_cmd_clear_cache(actv_cmd);

  res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn,
    actv_cmd);
  if (res < 0) {
    xerrno = errno;
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "error sending %s to backend: %s", (char *) actv_cmd->argv[0],
      strerror(xerrno));

    proxy_inet_close(session.pool, proxy_sess->backend_data_conn);
    proxy_sess->backend_data_conn = NULL;

    pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0],
      strerror(xerrno));
    pr_response_flush(&resp_err_list);

    errno = xerrno;
    return -1;
  }

  resp = proxy_ftp_ctrl_recv_resp(cmd->tmp_pool, proxy_sess->backend_ctrl_conn,
    &resp_nlines, flags);
  if (resp == NULL) {
    xerrno = errno;
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "error receiving %s response from backend: %s",
      (char *) actv_cmd->argv[0], strerror(xerrno));

    proxy_inet_close(session.pool, proxy_sess->backend_data_conn);
    proxy_sess->backend_data_conn = NULL;

    pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0],
      strerror(xerrno));
    pr_response_flush(&resp_err_list);

    errno = xerrno;
    return -1;
  }

  if (resp->num[0] != '2') {
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "received non-2xx response from backend for %s: %s %s",
      (char *) actv_cmd->argv[0], resp->num, resp->msg);

    proxy_inet_close(session.pool, proxy_sess->backend_data_conn);
    proxy_sess->backend_data_conn = NULL;

    if (policy_id == PR_CMD_EPRT_ID) {
      /* If using EPRT failed, try again using PORT, and switch the
       * DataTransferPolicy (if EPRT) to be PORT, for future attempts.
       */

      if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) {
        pr_trace_msg(trace_channel, 15,
          "falling back from EPRT to PORT DataTransferPolicy");
        proxy_sess->dataxfer_policy = PR_CMD_PORT_ID;
      }

      return proxy_ftp_xfer_prepare_active(PR_CMD_PORT_ID, cmd,
        error_code, proxy_sess, flags);
    }

    pr_response_add_err(error_code, "%s", resp->msg);
    pr_response_flush(&resp_err_list);

    errno = EINVAL;
    return -1;
  }

  return 0;
}
Example #7
0
static int forward_connect(pool *p, struct proxy_session *proxy_sess,
    pr_response_t **resp, unsigned int *resp_nlines) {
  conn_t *server_conn = NULL;
  int banner_ok = TRUE, use_tls, xerrno = 0;
  pr_netaddr_t *dst_addr;
  array_header *other_addrs = NULL;

  dst_addr = proxy_sess->dst_addr;
  other_addrs = proxy_sess->other_addrs;

  server_conn = proxy_conn_get_server_conn(p, proxy_sess, dst_addr);
  if (server_conn == NULL) {
    xerrno = errno;

    if (other_addrs != NULL) {
      register unsigned int i;

      /* Try the other IP addresses for the requested name (if any) as well. */
      for (i = 0; i < other_addrs->nelts; i++) {
        dst_addr = ((pr_netaddr_t **) other_addrs->elts)[i];

        pr_trace_msg(trace_channel, 8,
          "attempting to connect to other address #%u (%s) for requested "
          "URI '%.100s'", i+1, pr_netaddr_get_ipstr(dst_addr),
          proxy_conn_get_uri(proxy_sess->dst_pconn));
        server_conn = proxy_conn_get_server_conn(p, proxy_sess, dst_addr);
        if (server_conn != NULL) {
          proxy_sess->dst_addr = dst_addr;
          break;
        }
      }
    }

    if (server_conn == NULL) {
      xerrno = errno;

      /* EINVALs lead to strange-looking error responses; change them to
       * EPERM.
       */
      if (xerrno == EINVAL) {
        xerrno = EPERM;
      }
    }

    errno = xerrno;
    return -1;
  }

  /* XXX Support/send a CLNT command of our own?  Configurable via e.g.
   * "UserAgent" string?
   */

  proxy_sess->frontend_ctrl_conn = session.c;
  proxy_sess->backend_ctrl_conn = server_conn;

  /* Read the response from the backend server. */
  *resp = proxy_ftp_ctrl_recv_resp(p, proxy_sess->backend_ctrl_conn,
    resp_nlines);
  if (*resp == NULL) {
    xerrno = errno;

    pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "unable to read banner from server %s:%u: %s",
      pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr),
      ntohs(pr_netaddr_get_port(proxy_sess->backend_ctrl_conn->remote_addr)),
      strerror(xerrno));

    errno = EPERM;
    return -1;
  }

  if ((*resp)->num[0] != '2') {
    banner_ok = FALSE;
  }

  (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
    "received banner from backend %s:%u%s: %s %s",
    pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr),
    ntohs(pr_netaddr_get_port(proxy_sess->backend_ctrl_conn->remote_addr)),
    banner_ok ? "" : ", DISCONNECTING", (*resp)->num, (*resp)->msg);

  if (banner_ok == FALSE) {
    pr_inet_close(p, proxy_sess->backend_ctrl_conn);
    proxy_sess->backend_ctrl_conn = NULL;

    errno = EPERM;
    return -1;
  }

  /* Get the features supported by the backend server */
  if (proxy_ftp_sess_get_feat(p, proxy_sess) < 0) {
    if (errno != EPERM) {
      (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
        "unable to determine features of backend server: %s", strerror(errno));
    }
  }

  use_tls = proxy_tls_use_tls();
  if (use_tls != PROXY_TLS_ENGINE_OFF) {
    if (proxy_ftp_sess_send_auth_tls(p, proxy_sess) < 0) {
      xerrno = errno;

      (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
        "error enabling TLS on control connection to backend server: %s",
        strerror(xerrno));
      pr_inet_close(p, proxy_sess->backend_ctrl_conn);
      proxy_sess->backend_ctrl_conn = NULL;

      *resp = NULL;
      errno = xerrno;
      return -1;
    }
  }

  if (proxy_netio_postopen(server_conn->instrm) < 0) {
    xerrno = errno;

    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "postopen error for backend control connection input stream: %s",
      strerror(xerrno));
    proxy_inet_close(session.pool, server_conn);
    proxy_sess->backend_ctrl_conn = NULL;

    *resp = NULL;
    errno = xerrno;
    return -1;
  }

  if (proxy_netio_postopen(server_conn->outstrm) < 0) {
    xerrno = errno;

    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "postopen error for backend control connection output stream: %s",
      strerror(xerrno));
    proxy_inet_close(session.pool, server_conn);
    proxy_sess->backend_ctrl_conn = NULL;

    *resp = NULL;
    errno = xerrno;
    return -1;
  }

  if (use_tls != PROXY_TLS_ENGINE_OFF) {
    if (proxy_sess_state & PROXY_SESS_STATE_BACKEND_HAS_CTRL_TLS) {
      /* NOTE: should this be a fatal error? */
      (void) proxy_ftp_sess_send_pbsz_prot(p, proxy_sess);
    }
  }

  (void) proxy_ftp_sess_send_host(p, proxy_sess);

  proxy_sess_state |= PROXY_SESS_STATE_CONNECTED;
  return 0;
}