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); }
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; }
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); }
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; }