Пример #1
0
END_TEST

START_TEST (inet_conn_info_test) {
  int sockfd = -1, port = INPORT_ANY, res;
  conn_t *conn;

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

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

  res = pr_inet_get_conn_info(conn, -1);
  fail_unless(res < 0, "Failed to handle bad file descriptor");
  fail_unless(errno == EBADF, "Expected EBADF (%d), got %s (%d)", EBADF,
    strerror(errno), errno);

  res = pr_inet_get_conn_info(conn, 1);
  fail_unless(res < 0, "Failed to handle bad file descriptor");
  fail_unless(errno == ENOTSOCK, "Expected ENOTSOCK (%d), got %s (%d)",
    ENOTSOCK, strerror(errno), errno);

  pr_inet_close(p, conn);
}
Пример #2
0
static int init_inetd_bindings(void) {
  int res = 0;
  server_rec *serv = NULL;
  unsigned char *default_server = NULL, is_default = FALSE;

  /* We explicitly do NOT use the get_listening_conn() function here, since
   * inetd-run daemons will not a) handle restarts, and thus b) will not have
   * already-open connections to choose from.
   */

  main_server->listen = pr_inet_create_conn(main_server->pool, STDIN_FILENO,
    NULL, INPORT_ANY, FALSE);
  if (main_server->listen == NULL) {
    return -1;
  }

  /* Note: Since we are being called via inetd/xinetd, any socket options
   * which may be attempted by listeners for this event may not work.
   */
  pr_inet_generate_socket_event("core.ctrl-listen", main_server,
    main_server->addr, main_server->listen->listen_fd);

  /* Fill in all the important connection information. */
  if (pr_inet_get_conn_info(main_server->listen, STDIN_FILENO) == -1) {
    int xerrno = errno;

    pr_log_pri(PR_LOG_WARNING, "fatal: unable to get connection info: %s",
      strerror(xerrno));

    if (xerrno == ENOTSOCK) {
      pr_log_pri(PR_LOG_ERR, "(Running from command line? "
        "Use `ServerType standalone' in config file!)");
    }

    exit(1);
  }

  default_server = get_param_ptr(main_server->conf, "DefaultServer", FALSE);
  if (default_server != NULL &&
      *default_server == TRUE) {
    is_default = TRUE;
  }

  PR_CREATE_IPBIND(main_server, main_server->addr, main_server->ServerPort);
  PR_OPEN_IPBIND(main_server->addr, main_server->ServerPort,
    main_server->listen, is_default, TRUE, TRUE);
  PR_ADD_IPBINDS(main_server);

  /* Now attach the faked connection to all virtual servers. */
  for (serv = main_server->next; serv; serv = serv->next) {

    /* Because this server is sharing the connection with the
     * main server, we need a cleanup handler to remove
     * the server's reference when the original connection's
     * pool is destroyed.
     */

    serv->listen = main_server->listen;
    register_cleanup(serv->listen->pool, &serv->listen, server_cleanup_cb,
      server_cleanup_cb);

    is_default = FALSE;
    default_server = get_param_ptr(serv->conf, "DefaultServer", FALSE);
    if (default_server != NULL &&
        *default_server == TRUE) {
      is_default = TRUE;
    }

    PR_CREATE_IPBIND(serv, serv->addr, serv->ServerPort);
    PR_OPEN_IPBIND(serv->addr, serv->ServerPort, serv->listen, is_default,
      FALSE, TRUE);
    PR_ADD_IPBINDS(serv);
  }

  return 0;
}
Пример #3
0
char *pr_ident_lookup(pool *p, conn_t *c) {
  char *ret = "UNKNOWN";
  pool *tmp_pool = NULL;
  conn_t *ident_conn = NULL, *ident_io = NULL;
  char buf[256] = {'\0'}, *tok = NULL, *tmp = NULL;
  int timerno, i = 0;
  int ident_port = pr_inet_getservport(p, "ident", "tcp");

  tmp_pool = make_sub_pool(p);
  ident_timeout = 0;
  nstrm = NULL;

  if (ident_port == -1) {
    destroy_pool(tmp_pool);
    return pstrdup(p, ret);
  }

  /* Set up our timer before going any further. */
  timerno = pr_timer_add(PR_TUNABLE_TIMEOUTIDENT, -1, NULL,
    (callback_t) ident_timeout_cb, "ident lookup");
  if (timerno <= 0) {
    destroy_pool(tmp_pool);
    return pstrdup(p, ret);
  }

  ident_conn = pr_inet_create_connection(tmp_pool, NULL, -1, c->local_addr,
    INPORT_ANY, FALSE);
  pr_inet_set_nonblock(tmp_pool, ident_conn);

  i = pr_inet_connect_nowait(tmp_pool, ident_conn, c->remote_addr, ident_port);
  if (i < 0) {
    int xerrno = errno;

    pr_timer_remove(timerno, ANY_MODULE);
    pr_inet_close(tmp_pool, ident_conn);
    pr_trace_msg(trace_channel, 5, "connection to %s, port %d failed: %s",
      pr_netaddr_get_ipstr(c->remote_addr), ident_port, strerror(xerrno));

    destroy_pool(tmp_pool);

    errno = xerrno;
    return pstrdup(p, ret);
  }

  if (!i) {
    /* Not yet connected. */
    nstrm = pr_netio_open(p, PR_NETIO_STRM_OTHR, ident_conn->listen_fd,
      PR_NETIO_IO_RD);
    pr_netio_set_poll_interval(nstrm, 1);

    switch (pr_netio_poll(nstrm)) {

      /* Aborted, timed out */
      case 1: {
        if (ident_timeout) {
          pr_timer_remove(timerno, ANY_MODULE);
          pr_netio_close(nstrm);
          pr_inet_close(tmp_pool, ident_conn);

          pr_trace_msg(trace_channel, 5, "lookup timed out, returning '%s'",
            ret);
          destroy_pool(tmp_pool);
          return pstrdup(p, ret);
        }
        break;
      }

      /* Error. */
      case -1: {
        int xerrno = errno;

        pr_timer_remove(timerno, ANY_MODULE);
        pr_netio_close(nstrm);
        pr_inet_close(tmp_pool, ident_conn);

        pr_trace_msg(trace_channel, 6, "lookup failed (%s), returning '%s'",
          strerror(xerrno), ret);
        destroy_pool(tmp_pool);

        errno = xerrno;
        return pstrdup(p, ret);
      }

      /* Connected. */
      default: {
        ident_conn->mode = CM_OPEN;

        if (pr_inet_get_conn_info(ident_conn, ident_conn->listen_fd) < 0) {
          int xerrno = errno;

          pr_timer_remove(timerno, ANY_MODULE);
          pr_netio_close(nstrm);
          pr_inet_close(tmp_pool, ident_conn);

          pr_trace_msg(trace_channel, 2,
            "lookup timed out (%s), returning '%s'", strerror(xerrno), ret);
          destroy_pool(tmp_pool);

          errno = xerrno;
          return pstrdup(p, ret);
        }
        break;
      }
    }
  }

  ident_io = pr_inet_openrw(tmp_pool, ident_conn, NULL, PR_NETIO_STRM_OTHR,
    -1, -1, -1, FALSE);
  if (ident_io == NULL) {
    int xerrno = errno;

    pr_timer_remove(timerno, ANY_MODULE);
    pr_inet_close(tmp_pool, ident_conn);

    pr_trace_msg(trace_channel, 3, "failed opening read/write connection: %s",
      strerror(xerrno));
    destroy_pool(tmp_pool);

    errno = xerrno;
    return pstrdup(p, ret);
  }

  nstrm = ident_io->instrm;

  pr_inet_set_nonblock(tmp_pool, ident_io);
  pr_netio_set_poll_interval(ident_io->instrm, 1);
  pr_netio_set_poll_interval(ident_io->outstrm, 1);

  pr_netio_printf(ident_io->outstrm, "%d, %d\r\n", c->remote_port,
    c->local_port);

  /* If the timer fires while in netio_gets(), netio_gets() will simply return
   * either a partial string, or NULL.  This works because ident_timeout_cb
   * aborts the stream from which we are reading.  netio_set_poll_interval() is
   * used to make sure significant delays don't occur on systems that
   * automatically restart syscalls after the SIGALRM signal.
   */

  pr_trace_msg(trace_channel, 4, "reading response from remote ident server");

  if (pr_netio_gets(buf, sizeof(buf), ident_io->instrm)) {
    strip_end(buf, "\r\n");

    pr_trace_msg(trace_channel, 6, "received '%s' from remote ident server",
      buf);

    tmp = buf;
    tok = get_token(&tmp, ":");
    if (tok && (tok = get_token(&tmp, ":"))) {
      while (*tok && isspace((int) *tok)) {
        pr_signals_handle();
        tok++;
      }
      strip_end(tok, " \t");

      if (strcasecmp(tok, "ERROR") == 0) {
        if (tmp) {
          while (*tmp && isspace((int) *tmp)) {
            pr_signals_handle();
            tmp++;
          }
	  strip_end(tmp, " \t");
          if (strcasecmp(tmp, "HIDDEN-USER") == 0)
            ret = "HIDDEN-USER";
        }

      } else if (strcasecmp(tok, "USERID") == 0) {
        if (tmp && (tok = get_token(&tmp, ":"))) {
          if (tmp) {
            while (*tmp && isspace((int) *tmp)) {
              pr_signals_handle();
              tmp++;
            }
            strip_end(tmp, " \t");
            ret = tmp;
          }
        }
      }
    }
  }

  pr_timer_remove(timerno, ANY_MODULE);
  pr_inet_close(tmp_pool, ident_io);
  pr_inet_close(tmp_pool, ident_conn);
  destroy_pool(tmp_pool);

  return pstrdup(p, ret);
}
Пример #4
0
conn_t *proxy_conn_get_server_conn(pool *p, struct proxy_session *proxy_sess,
    pr_netaddr_t *remote_addr) {
  pr_netaddr_t *bind_addr = NULL, *local_addr = NULL;
  const char *remote_ipstr = NULL;
  unsigned int remote_port;
  conn_t *server_conn, *ctrl_conn;
  int res;

  if (proxy_sess->connect_timeout > 0) {
    const char *notes_key = "mod_proxy.proxy-connect-address";

    proxy_sess->connect_timerno = pr_timer_add(proxy_sess->connect_timeout,
      -1, &proxy_module, proxy_conn_connect_timeout_cb, "ProxyTimeoutConnect");

    (void) pr_table_remove(session.notes, notes_key, NULL);

    if (pr_table_add(session.notes, notes_key, remote_addr,
        sizeof(pr_netaddr_t)) < 0) {
      (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
        "error stashing proxy connect address note: %s", strerror(errno));
    }
  }

  remote_ipstr = pr_netaddr_get_ipstr(remote_addr);
  remote_port = ntohs(pr_netaddr_get_port(remote_addr));

  /* Check the family of the retrieved address vs what we'll be using
   * to connect.  If there's a mismatch, we need to get an addr with the
   * matching family.
   */

  if (pr_netaddr_get_family(session.c->local_addr) == pr_netaddr_get_family(remote_addr)) {
    local_addr = session.c->local_addr;

  } else {
    /* In this scenario, the proxy has an IPv6 socket, but the remote/backend
     * server has an IPv4 (or IPv4-mapped IPv6) address.  OR it's the proxy
     * which has an IPv4 socket, and the remote/backend server has an IPv6
     * address.
     */
    if (pr_netaddr_get_family(session.c->local_addr) == AF_INET) {
      char *ip_str;

      /* Convert the local address from an IPv4 to an IPv6 addr. */
      ip_str = pcalloc(p, INET6_ADDRSTRLEN + 1);
      snprintf(ip_str, INET6_ADDRSTRLEN, "::ffff:%s",
        pr_netaddr_get_ipstr(session.c->local_addr));
      local_addr = pr_netaddr_get_addr(p, ip_str, NULL);

    } else {
      local_addr = pr_netaddr_v6tov4(p, session.c->local_addr);
      if (local_addr == NULL) {
        pr_trace_msg(trace_channel, 4,
          "error converting IPv6 local address %s to IPv4 address: %s",
          pr_netaddr_get_ipstr(session.c->local_addr), strerror(errno));
      }
    }

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

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

  /* Note: IF mod_proxy is running on localhost, and the connection to be
   * made is to a public IP address, then this connect(2) attempt would most
   * likely fail with ENETUNREACH, since localhost is a loopback network,
   * and of course not reachable from a public IP.  Thus we check for this
   * edge case (which happens often for development).
   */
  if (pr_netaddr_is_loopback(bind_addr) == TRUE) {
    const char *local_name;
    pr_netaddr_t *local_addr;

    local_name = pr_netaddr_get_localaddr_str(p);
    local_addr = pr_netaddr_get_addr(p, local_name, NULL);

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

  server_conn = pr_inet_create_conn(p, -1, bind_addr, INPORT_ANY, FALSE);
  if (server_conn == NULL) {
    int xerrno = errno;

    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "error creating connection to %s: %s", pr_netaddr_get_ipstr(bind_addr),
      strerror(xerrno));

    pr_timer_remove(proxy_sess->connect_timerno, &proxy_module);
    errno = xerrno;
    return NULL;
  }

  pr_trace_msg(trace_channel, 11, "connecting to backend address %s#%u from %s",
    remote_ipstr, remote_port, pr_netaddr_get_ipstr(bind_addr));

  res = pr_inet_connect_nowait(p, server_conn, remote_addr,
    ntohs(pr_netaddr_get_port(remote_addr)));
  if (res < 0) {
    int xerrno = errno;

    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "error starting connect to %s#%u: %s", remote_ipstr, remote_port,
      strerror(xerrno));

    pr_timer_remove(proxy_sess->connect_timerno, &proxy_module);
    errno = xerrno;
    return NULL;
  }

  if (res == 0) {
    pr_netio_stream_t *nstrm;
    int nstrm_mode = PR_NETIO_IO_RD;

    if (proxy_opts & PROXY_OPT_USE_PROXY_PROTOCOL) {
      /* Rather than waiting for the stream to be readable (because the
       * other end sent us something), wait for the stream to be writable
       * so that we can send something to the other end).
       */
      nstrm_mode = PR_NETIO_IO_WR;
    }

    /* Not yet connected. */
    nstrm = proxy_netio_open(p, PR_NETIO_STRM_OTHR, server_conn->listen_fd,
      nstrm_mode);
    if (nstrm == NULL) {
      int xerrno = errno;

      (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
        "error opening stream to %s#%u: %s", remote_ipstr, remote_port,
        strerror(xerrno));

      pr_timer_remove(proxy_sess->connect_timerno, &proxy_module);
      pr_inet_close(p, server_conn);

      errno = xerrno;
      return NULL;
    }

    proxy_netio_set_poll_interval(nstrm, 1);

    switch (proxy_netio_poll(nstrm)) {
      case 1: {
        /* Aborted, timed out.  Note that we shouldn't reach here. */
        pr_timer_remove(proxy_sess->connect_timerno, &proxy_module);
        proxy_netio_close(nstrm);
        pr_inet_close(p, server_conn);

        errno = ETIMEDOUT;
        return NULL;
      }

      case -1: {
        /* Error */
        int xerrno = errno;

        (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
          "error connecting to %s#%u: %s", remote_ipstr, remote_port,
          strerror(xerrno));

        pr_timer_remove(proxy_sess->connect_timerno, &proxy_module);
        proxy_netio_close(nstrm);
        pr_inet_close(p, server_conn);

        errno = xerrno;
        return NULL;
      }

      default: {
        /* Connected */
        server_conn->mode = CM_OPEN;
        pr_timer_remove(proxy_sess->connect_timerno, &proxy_module);
        pr_table_remove(session.notes, "mod_proxy.proxy-connect-addr", NULL);

        res = pr_inet_get_conn_info(server_conn, server_conn->listen_fd);
        if (res < 0) {
          int xerrno = errno;

          (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
            "error obtaining local socket info on fd %d: %s",
            server_conn->listen_fd, strerror(xerrno));

          proxy_netio_close(nstrm);
          pr_inet_close(p, server_conn);

          errno = xerrno;
          return NULL;
        }

        break;
      }
    }
  }

  pr_trace_msg(trace_channel, 5,
    "successfully connected to %s#%u from %s#%d", remote_ipstr, remote_port,
    pr_netaddr_get_ipstr(server_conn->local_addr),
    ntohs(pr_netaddr_get_port(server_conn->local_addr)));

  ctrl_conn = proxy_inet_openrw(p, server_conn, NULL, PR_NETIO_STRM_CTRL, -1,
    -1, -1, FALSE);
  if (ctrl_conn == NULL) {
    int xerrno = errno;

    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "unable to open control connection to %s#%u: %s", remote_ipstr,
      remote_port, strerror(xerrno));

    pr_inet_close(p, server_conn);

    errno = xerrno;
    return NULL;
  }

  return ctrl_conn;
}