/*
* ProFTPD - FTP server daemon
* Copyright (c) 2001, 2002, 2003 The ProFTPD Project team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* As a special exemption, The ProFTPD Project team and other respective
* copyright holders give permission to link this program with OpenSSL, and
* distribute the resulting executable, without including the source code for
* OpenSSL in the source distribution.
*/
/* Routines to work with ProFTPD bindings
*
* $Id: bindings.c,v 1.30 2005/06/21 20:54:23 castaglia Exp $
*/
#include "conf.h"
/* Some convenience macros */
#define PR_CLOSE_NAMEBIND(n, a, p) \
if ((res = pr_namebind_close((n), (a), (p))) < 0) \
pr_log_pri(PR_LOG_NOTICE, \
"%s:%d: notice, unable to close namebind '%s': %s", \
__FILE__, __LINE__, (n), strerror(errno))
#define PR_CREATE_NAMEBIND(s, n, a, p) \
if ((res = pr_namebind_create((s), (n), (a), (p))) < 0) \
pr_log_pri(PR_LOG_NOTICE, \
"%s:%d: notice: unable to create namebind '%s': %s", \
__FILE__, __LINE__, (n), strerror(errno))
#define PR_OPEN_NAMEBIND(n, a, p) \
if ((res = pr_namebind_open((n), (a), (p))) < 0) \
pr_log_pri(PR_LOG_NOTICE, \
"%s:%d: notice: unable to open namebind '%s': %s", \
__FILE__, __LINE__, (n), strerror(errno))
/* From src/dirtree.c */
extern xaset_t *server_list;
extern server_rec *main_server;
static pr_ipbind_t *ipbind_table[PR_BINDINGS_TABLE_SIZE];
static pool *binding_pool = NULL;
static pr_ipbind_t *ipbind_default_server = NULL,
*ipbind_localhost_server = NULL;
#ifdef PR_USE_IPV6
static int ipv6_supported = TRUE;
#endif /* PR_USE_IPV6 */
/* Server cleanup callback function */
static void server_cleanup_cb(void *conn) {
*((conn_t **) conn) = NULL;
}
/* The hashing function for the hash table of bindings. This algorithm
* is stolen from Apache's http_vhost.c
*/
static unsigned int ipbind_hash_addr(pr_netaddr_t *addr) {
size_t offset = pr_netaddr_get_inaddr_len(addr);
/* The key is the last four bytes of the IP address.
* For IPv4, this is the entire address, as always.
* For IPv6, this is usually part of the MAC address.
*/
unsigned int key = *(unsigned *) ((char *) pr_netaddr_get_inaddr(addr) +
offset - 4);
key ^= (key >> 16);
return ((key >> 8) ^ key) % PR_BINDINGS_TABLE_SIZE;
}
/* Slight (clever?) optimization: the loop in server_loop() always
* calls pr_ipbind_listen(), selects, then pr_ipbind_accept_conn(). Now,
* rather than having both pr_ipbind_listen() and pr_ipbind_accept_conn()
* scan the entire ipbind table looking for matches, what if pr_ipbind_listen
* kept track of which listeners (connt_s) it used, so that
* pr_ipbind_accept_conn() need merely check those listeners, rather than
* scanning the entire table itself?
*/
static array_header *listener_list = NULL;
conn_t *pr_ipbind_accept_conn(fd_set *readfds, int *listenfd) {
conn_t **listeners = listener_list->elts;
register unsigned int i = 0;
/* sanity checks */
if (!readfds) {
errno = EINVAL;
return NULL;
}
if (!listenfd) {
errno = EINVAL;
return NULL;
}
for (i = 0; i < listener_list->nelts; i++) {
conn_t *listener = listeners[i];
pr_signals_handle();
if (FD_ISSET(listener->listen_fd, readfds) &&
listener->mode == CM_LISTEN) {
int fd = pr_inet_accept_nowait(listener->pool, listener);
if (fd == -1) {
/* Handle errors gracefully. If we're here, then
* ipbind->ib_server->listen contains either error information, or
* we just got caught in a blocking condition.
*/
if (listener->mode == CM_ERROR) {
pr_log_pri(PR_LOG_ERR, "error: unable to accept an incoming "
"connection (%s)", strerror(listener->xerrno));
listener->xerrno = 0;
listener->mode = CM_LISTEN;
return NULL;
}
}
*listenfd = fd;
return listener;
}
}
errno = ENOENT;
return NULL;
}
int pr_ipbind_add_binds(server_rec *serv) {
int res = 0;
config_rec *c = NULL;
conn_t *listen_conn = NULL;
pr_netaddr_t *addr = NULL;
/* sanity check */
if (!serv)
return -1;
c = find_config(serv->conf, CONF_PARAM, "_bind", FALSE);
while (c) {
listen_conn = NULL;
addr = pr_netaddr_get_addr(serv->pool, c->argv[0], NULL);
if (!addr) {
pr_log_pri(PR_LOG_NOTICE,
"notice: unable to determine IP address of '%s'", (char *) c->argv[0]);
c = find_config_next(c, c->next, CONF_PARAM, "_bind", FALSE);
continue;
}
/* If the SocketBindTight directive is in effect, create a separate
* listen socket for this address, and add it to the binding list.
*/
if (SocketBindTight && serv->ServerPort) {
listen_conn = pr_inet_create_connection(serv->pool, server_list, -1, addr,
serv->ServerPort, FALSE);
PR_CREATE_IPBIND(serv, addr);
PR_OPEN_IPBIND(addr, serv->ServerPort, listen_conn, FALSE, FALSE, TRUE);
} else {
PR_CREATE_IPBIND(serv, addr);
PR_OPEN_IPBIND(addr, serv->ServerPort, serv->listen, FALSE, FALSE, TRUE);
}
/* Move on to the next bind directive */
c = find_config_next(c, c->next, CONF_PARAM, "_bind", FALSE);
}
/* done */
return 0;
}
int pr_ipbind_close(pr_netaddr_t *addr, unsigned int port,
unsigned char close_namebinds) {
int res = 0;
register unsigned int i = 0;
if (addr) {
pr_ipbind_t *ipbind = NULL;
unsigned char have_ipbind = FALSE;
i = ipbind_hash_addr(addr);
if (ipbind_table[i] == NULL) {
pr_log_pri(PR_LOG_NOTICE, "notice: no ipbind found for %s:%d",
pr_netaddr_get_ipstr(addr), port);
errno = ENOENT;
return -1;
}
for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) {
if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0 &&
(!ipbind->ib_port || ipbind->ib_port == port)) {
have_ipbind = TRUE;
break;
}
}
if (!have_ipbind) {
pr_log_pri(PR_LOG_NOTICE, "notice: no ipbind found for %s:%d",
pr_netaddr_get_ipstr(addr), port);
errno = ENOENT;
return -1;
}
/* If already closed, exit now. */
if (!ipbind->ib_isactive) {
errno = EPERM;
return -1;
}
/* Close the ipbinding's listen connection, if present. The trick
* here is determining whether this binding's listen member is
* _the_ listening socket for the master daemon, or whether it's
* been created for SocketBindTight, and can be closed.
*
* Actually, it's not that hard. It's only _the_ listening socket
* for the master daemon in inetd mode, in which case virtual servers
* can't be shutdown via ftpdctl, anyway.
*/
if (SocketBindTight && ipbind->ib_listener != NULL) {
pr_inet_close(ipbind->ib_server->pool, ipbind->ib_listener);
ipbind->ib_listener = ipbind->ib_server->listen = NULL;
}
/* Mark this ipbind as inactive. For SocketBindTight sockets, the
* closing of the listening connection will suffice, from the clients'
* point of view. However, this covers the non-SocketBindTight case,
* and will prevent this binding from returning its server_rec pointer
* on future lookup requests via pr_ipbind_get_server().
*/
ipbind->ib_isactive = FALSE;
if (close_namebinds && ipbind->ib_namebinds) {
register unsigned int j = 0;
pr_namebind_t **namebinds = NULL;
namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts;
for (j = 0; j < ipbind->ib_namebinds->nelts; j++) {
pr_namebind_t *nb = namebinds[j];
PR_CLOSE_NAMEBIND(nb->nb_name, nb->nb_server->addr,
nb->nb_server->ServerPort);
}
}
} else {
/* A NULL addr has a special meaning: close _all_ ipbinds in the
* list.
*/
for (i = 0; i < PR_BINDINGS_TABLE_SIZE; i++) {
pr_ipbind_t *ipbind = NULL;
for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) {
if (SocketBindTight && ipbind->ib_listener != NULL) {
pr_inet_close(main_server->pool, ipbind->ib_listener);
ipbind->ib_listener = ipbind->ib_server->listen = NULL;
}
/* Note: do not need to check if this ipbind was previously closed,
* for the NULL addr is a request to shut down all ipbinds,
* regardless of their current state.
*/
ipbind->ib_isactive = FALSE;
if (close_namebinds && ipbind->ib_namebinds) {
register unsigned int j = 0;
pr_namebind_t **namebinds = NULL;
namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts;
for (j = 0; j < ipbind->ib_namebinds->nelts; j++) {
pr_namebind_t *nb = namebinds[j];
PR_CLOSE_NAMEBIND(nb->nb_name, nb->nb_server->addr,
nb->nb_server->ServerPort);
}
}
}
}
}
/* Done */
return 0;
}
/* Need a way to close all listening fds in a child process. */
int pr_ipbind_close_listeners(void) {
conn_t **listeners;
register unsigned int i = 0;
/* sanity checks */
if (!listener_list ||
listener_list->nelts == 0)
return 0;
listeners = listener_list->elts;
for (i = 0; i < listener_list->nelts; i++) {
conn_t *listener = listeners[i];
pr_signals_handle();
if (listener->listen_fd != -1) {
close(listener->listen_fd);
listener->listen_fd = -1;
}
}
return 0;
}
int pr_ipbind_create(server_rec *server, pr_netaddr_t *addr) {
int res = 0;
pr_ipbind_t *ipbind = NULL;
config_rec *c = NULL;
server_rec *s = NULL;
register unsigned int i = 0;
/* Sanity checks */
if (!server || !addr) {
errno = EINVAL;
return -1;
}
i = ipbind_hash_addr(addr);
/* Make sure the address is not already in use */
for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) {
if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0 &&
ipbind->ib_port == server->ServerPort) {
/* An ipbind already exists for this IP address */
pr_log_pri(PR_LOG_NOTICE, "notice: '%s' (%s:%u) already bound to '%s'",
server->ServerName, pr_netaddr_get_ipstr(addr), server->ServerPort,
ipbind->ib_server->ServerName);
errno = EADDRINUSE;
return -1;
}
}
if (!binding_pool) {
/* Initialize the working pool, if not present */
binding_pool = make_sub_pool(permanent_pool);
pr_pool_tag(binding_pool, "Bindings Pool");
}
ipbind = pcalloc(server->pool, sizeof(pr_ipbind_t));
ipbind->ib_server = server;
ipbind->ib_addr = addr;
ipbind->ib_port = server->ServerPort;
ipbind->ib_namebinds = NULL;
ipbind->ib_isdefault = FALSE;
ipbind->ib_islocalhost = FALSE;
ipbind->ib_isactive = FALSE;
/* Add the ipbind to the table. */
if (ipbind_table[i])
ipbind->ib_next = ipbind_table[i];
ipbind_table[i] = ipbind;
/* Create any namebinds associated with this server. */
c = find_config(server->conf, CONF_NAMED, NULL, FALSE);
while (c) {
s = (server_rec *) c->argv[0];
PR_CREATE_NAMEBIND(s, c->name, server->addr, server->ServerPort);
c = find_config_next(c, c->next, CONF_NAMED, NULL, FALSE);
}
return 0;
}
pr_ipbind_t *pr_ipbind_find(pr_netaddr_t *addr, unsigned int port,
unsigned char skip_inactive) {
pr_ipbind_t *ipbind = NULL;
register unsigned int i = ipbind_hash_addr(addr);
for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) {
if (skip_inactive && !ipbind->ib_isactive)
continue;
if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0 &&
(!ipbind->ib_port || ipbind->ib_port == port))
return ipbind;
}
/* default return value */
return NULL;
}
pr_ipbind_t *pr_ipbind_get(pr_ipbind_t *prev) {
static unsigned int i = 0;
if (prev) {
/* If there's another ipbind in this chain, simply return that. */
if (prev->ib_next)
return prev->ib_next;
/* If the increment is at the maximum size, return NULL (no more chains
* to be examined).
*/
if (i == PR_BINDINGS_TABLE_SIZE)
return NULL;
/* Increment the index. At this point, we know that the given pointer is
* the last in the chain, and that there are more chains in the table
* to be examined.
*/
i++;
} else
/* Reset the index if prev is NULL. */
i = 0;
/* Search for the next non-empty chain in the table. */
for (; i < PR_BINDINGS_TABLE_SIZE; i++) {
if (ipbind_table[i])
return ipbind_table[i];
}
return NULL;
}
server_rec *pr_ipbind_get_server(pr_netaddr_t *addr, unsigned int port) {
pr_ipbind_t *ipbind = NULL;
/* If we've got a binding configured for this exact address, return it
* straightaway.
*/
ipbind = pr_ipbind_find(addr, port, TRUE);
if (ipbind != NULL)
return ipbind->ib_server;
/* Use the default server, if set. */
if (ipbind_default_server && ipbind_default_server->ib_isactive) {
pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using "
"DefaultServer '%s'", pr_netaddr_get_ipstr(addr), port,
ipbind_default_server->ib_server->ServerName);
return ipbind_default_server->ib_server;
}
/* Not found in binding list, and no DefaultServer, so see if it's the
* loopback address
*/
if (ipbind_localhost_server && pr_netaddr_loopback(addr))
return ipbind_localhost_server->ib_server;
return NULL;
}
int pr_ipbind_listen(fd_set *readfds) {
int maxfd = 0;
register unsigned int i = 0;
/* sanity check */
if (!readfds)
return -1;
FD_ZERO(readfds);
if (!binding_pool) {
/* Initialize the working pool, if not present */
binding_pool = make_sub_pool(permanent_pool);
pr_pool_tag(binding_pool, "Bindings Pool");
}
/* Reset the listener list. */
if (!listener_list)
listener_list = make_array(binding_pool, 1, sizeof(conn_t *));
else
/* Nasty hack to "clear" the list by making it think it has no
* elements.
*/
listener_list->nelts = 0;
/* Slower than the hash lookup, but...we have to check each and every
* ipbind in the table.
*/
for (i = 0; i < PR_BINDINGS_TABLE_SIZE; i++) {
pr_ipbind_t *ipbind = NULL;
for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) {
/* Skip inactive bindings, but only if SocketBindTight is in effect. */
if (SocketBindTight && !ipbind->ib_isactive)
continue;
if (ipbind->ib_listener) {
if (ipbind->ib_listener->mode == CM_NONE)
pr_inet_listen(ipbind->ib_listener->pool, ipbind->ib_listener,
tcpBackLog);
if (ipbind->ib_listener->mode == CM_ACCEPT)
pr_inet_resetlisten(ipbind->ib_listener->pool, ipbind->ib_listener);
if (ipbind->ib_listener->mode == CM_LISTEN) {
FD_SET(ipbind->ib_listener->listen_fd, readfds);
if (ipbind->ib_listener->listen_fd > maxfd)
maxfd = ipbind->ib_listener->listen_fd;
/* Add this to the listener list as well. */
*((conn_t **) push_array(listener_list)) = ipbind->ib_listener;
}
}
}
}
return maxfd;
}
int pr_ipbind_open(pr_netaddr_t *addr, unsigned int port, conn_t *listen_conn,
unsigned char isdefault, unsigned char islocalhost,
unsigned char open_namebinds) {
int res = 0;
pr_ipbind_t *ipbind = NULL;
/* sanity checks */
if (!addr) {
errno = EINVAL;
return -1;
}
/* Find the binding for this server/address */
if ((ipbind = pr_ipbind_find(addr, port, FALSE)) == NULL) {
errno = ENOENT;
return -1;
}
if (listen_conn)
listen_conn->next = NULL;
ipbind->ib_listener = ipbind->ib_server->listen = listen_conn;
ipbind->ib_listener = listen_conn;
ipbind->ib_isdefault = isdefault;
ipbind->ib_islocalhost = islocalhost;
/* Stash a pointer to this ipbind, since it is designated as the
* default server (via the DefaultServer directive), for use in the
* lookup functions.
*/
/* Stash pointers to this ipbind for use in the lookup functions if:
*
* - It's the default server (specified via the DefaultServer directive)
* - It handles connections to the loopback interface
*/
if (isdefault)
ipbind_default_server = ipbind;
if (islocalhost)
ipbind_localhost_server = ipbind;
/* If requested, look for any namebinds for this ipbind, and open them. */
if (open_namebinds && ipbind->ib_namebinds) {
register unsigned int i = 0;
pr_namebind_t **namebinds = NULL;
/* NOTE: in the future, these namebinds may need to be stored/
* manipulated in hash tables themselves, but, for now, linked lists
* should suffice.
*/
namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts;
for (i = 0; i < ipbind->ib_namebinds->nelts; i++) {
pr_namebind_t *nb = namebinds[i];
PR_OPEN_NAMEBIND(nb->nb_name, nb->nb_server->addr,
nb->nb_server->ServerPort);
}
}
/* Mark this binding as now being active. */
ipbind->ib_isactive = TRUE;
return 0;
}
int pr_namebind_close(const char *name, pr_netaddr_t *addr,
unsigned int port) {
pr_namebind_t *namebind = NULL;
/* sanity checks */
if (!name || !addr) {
errno = EINVAL;
return -1;
}
/* find the requested namebind */
if ((namebind = pr_namebind_find(name, addr, port, FALSE)) == NULL) {
errno = ENOENT;
return -1;
}
/* Mark this binding as inactive */
namebind->nb_isactive = FALSE;
/* default return value */
return 0;
}
int pr_namebind_create(server_rec *server, const char *name,
pr_netaddr_t *addr, unsigned int port) {
pr_ipbind_t *ipbind = NULL;
pr_namebind_t *namebind = NULL, **namebinds = NULL;
/* sanity checks */
if (!server || !name) {
errno = EINVAL;
return -1;
}
/* First, find the ipbind to hold this namebind. */
ipbind = pr_ipbind_find(addr, port, FALSE);
if (ipbind == NULL) {
errno = ENOENT;
return -1;
}
/* Make sure we can add this namebind. */
if (!ipbind->ib_namebinds) {
ipbind->ib_namebinds = make_array(binding_pool, 0, sizeof(pr_namebind_t *));
} else {
register unsigned int i = 0;
namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts;
/* See if there is already a namebind for the given name. */
for (i = 0; i < ipbind->ib_namebinds->nelts; i++) {
namebind = namebinds[i];
if (namebind && namebind->nb_name && !strcmp(namebind->nb_name, name)) {
errno = EEXIST;
return -1;
}
}
}
/* Allocate a new namebind */
namebind = (pr_namebind_t *) pcalloc(server->pool, sizeof(pr_namebind_t));
namebind->nb_name = name;
namebind->nb_server = server;
namebind->nb_isactive = FALSE;
/* Inherit server fields from the container server */
namebind->nb_server->ServerAdmin = (namebind->nb_server->ServerAdmin ?
namebind->nb_server->ServerAdmin : server->ServerAdmin ?
server->ServerAdmin : main_server->ServerAdmin);
/* These three assignments enforce the use of DNS names as HOST names.
* Use of DNS names is not a requirement, so in order to be very flexible,
* these may need to change...
*/
namebind->nb_server->ServerName = (namebind->nb_server->ServerName ?
namebind->nb_server->ServerName : (char *) name);
namebind->nb_server->ServerAddress = (server->ServerAddress ?
server->ServerAddress : main_server->ServerAddress);
namebind->nb_server->ServerFQDN = (server->ServerFQDN ?
server->ServerFQDN : main_server->ServerFQDN);
namebind->nb_server->tcp_mss_len = (server->tcp_mss_len ?
server->tcp_mss_len : main_server->tcp_mss_len);
namebind->nb_server->tcp_rcvbuf_len = (server->tcp_rcvbuf_len ?
server->tcp_rcvbuf_len : main_server->tcp_rcvbuf_len);
namebind->nb_server->tcp_rcvbuf_override = (server->tcp_rcvbuf_override ?
TRUE : main_server->tcp_rcvbuf_override);
namebind->nb_server->tcp_sndbuf_len = (server->tcp_sndbuf_len ?
server->tcp_sndbuf_len : main_server->tcp_sndbuf_len);
namebind->nb_server->tcp_sndbuf_override = (server->tcp_sndbuf_override ?
TRUE : main_server->tcp_sndbuf_override);
namebind->nb_server->addr = (server->addr ? server->addr :
main_server->addr);
namebind->nb_server->ServerPort = (server->ServerPort ? server->ServerPort :
main_server->ServerPort);
namebind->nb_listener = (server->listen ? server->listen :
main_server->listen);
/* Add this namebind to the ipbind's list */
*((pr_namebind_t **) push_array(ipbind->ib_namebinds)) = namebind;
/* default return value */
return 0;
}
pr_namebind_t *pr_namebind_find(const char *name, pr_netaddr_t *addr,
unsigned int port, unsigned char skip_inactive) {
pr_ipbind_t *ipbind = NULL;
pr_namebind_t *namebind = NULL;
/* sanity checks */
if (!name || !addr) {
errno = EINVAL;
return NULL;
}
/* first, find an active ipbind for the given addr/port */
if ((ipbind = pr_ipbind_find(addr, port, skip_inactive)) == NULL) {
errno = ENOENT;
return NULL;
}
if (!ipbind->ib_namebinds) {
return NULL;
} else {
register unsigned int i = 0;
pr_namebind_t **namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts;
for (i = 0; i < ipbind->ib_namebinds->nelts; i++) {
namebind = namebinds[i];
/* skip inactive namebinds */
if (skip_inactive && namebind && !namebind->nb_isactive)
continue;
/* At present, this looks for an exactly matching name. In the future,
* we may want to have something like Apache's matching scheme, which
* looks for the most specific domain to the most general. Note that
* that scheme, however, is specific to DNS; should any other naming
* scheme be desired, that sort of matching will be unnecessary.
*/
if (namebind && namebind->nb_name && !strcmp(namebind->nb_name, name))
return namebind;
}
}
/* default return value */
return NULL;
}
server_rec *pr_namebind_get_server(const char *name, pr_netaddr_t *addr,
unsigned int port) {
pr_namebind_t *namebind = NULL;
/* Basically, just a wrapper around pr_namebind_find() */
if ((namebind = pr_namebind_find(name, addr, port, TRUE)) == NULL)
return NULL;
return namebind->nb_server;
}
int pr_namebind_open(const char *name, pr_netaddr_t *addr, unsigned int port) {
pr_namebind_t *namebind = NULL;
/* sanity checks */
if (!name || !addr) {
errno = EINVAL;
return -1;
}
/* Find the requested namebind */
if ((namebind = pr_namebind_find(name, addr, port, FALSE)) == NULL) {
errno = ENOENT;
return -1;
}
/* Mark this binding as active */
namebind->nb_isactive = TRUE;
/* Default return value */
return 0;
}
void free_bindings(void) {
if (binding_pool) {
destroy_pool(binding_pool);
binding_pool = NULL;
listener_list = NULL;
}
memset(ipbind_table, 0, sizeof(ipbind_table));
}
static void init_inetd_bindings(void) {
int res = 0;
server_rec *serv = NULL;
unsigned char *default_server = NULL, is_default = FALSE;
main_server->listen = pr_inet_create_connection(main_server->pool,
server_list, STDIN_FILENO, NULL, INPORT_ANY, FALSE);
/* Fill in all the important connection information. */
if (pr_inet_get_conn_info(main_server->listen, STDIN_FILENO) == -1) {
pr_log_pri(PR_LOG_ERR, "fatal: %s", strerror(errno));
if (errno == ENOTSOCK)
pr_log_pri(PR_LOG_ERR, "(Running from command line? "
"Use `ServerType standalone' in config file!)");
exit(1);
}
if ((default_server = get_param_ptr(main_server->conf, "DefaultServer",
FALSE)) != NULL && *default_server == TRUE)
is_default = TRUE;
PR_CREATE_IPBIND(main_server, main_server->addr);
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;
if ((default_server = get_param_ptr(serv->conf, "DefaultServer",
FALSE)) != NULL && *default_server == TRUE)
is_default = TRUE;
PR_CREATE_IPBIND(serv, serv->addr);
PR_OPEN_IPBIND(serv->addr, serv->ServerPort, serv->listen, is_default,
FALSE, TRUE);
PR_ADD_IPBINDS(serv);
}
return;
}
static void init_standalone_bindings(void) {
int res = 0;
server_rec *serv = NULL;
unsigned char *default_server = NULL, is_default = FALSE;
/* If a port is set to zero, the address/port is not bound to a socket
* at all.
*/
if (main_server->ServerPort) {
/* If SocketBindTight is off, then pr_inet_create_connection() will
* create and bind to a wildcard socket. However, should it be an
* IPv4 or an IPv6 wildcard socket?
*/
if (!SocketBindTight) {
#ifdef PR_USE_IPV6
if (ipv6_supported) {
pr_inet_set_default_family(NULL, AF_INET6);
} else {
pr_inet_set_default_family(NULL,
pr_netaddr_get_family(main_server->addr));
}
#else
pr_inet_set_default_family(NULL,
pr_netaddr_get_family(main_server->addr));
#endif /* PR_USE_IPV6 */
}
main_server->listen =
pr_inet_create_connection(main_server->pool, server_list, -1,
(SocketBindTight ? main_server->addr : NULL),
main_server->ServerPort, FALSE);
} else
main_server->listen = NULL;
if ((default_server = get_param_ptr(main_server->conf, "DefaultServer",
FALSE)) != NULL && *default_server == TRUE)
is_default = TRUE;
if (main_server->ServerPort || is_default) {
PR_CREATE_IPBIND(main_server, main_server->addr);
PR_OPEN_IPBIND(main_server->addr, main_server->ServerPort,
main_server->listen, is_default, TRUE, TRUE);
PR_ADD_IPBINDS(main_server);
}
for (serv = main_server->next; serv; serv = serv->next) {
if (serv->ServerPort != main_server->ServerPort || SocketBindTight ||
!main_server->listen) {
is_default = FALSE;
if ((default_server = get_param_ptr(serv->conf, "DefaultServer",
FALSE)) != NULL && *default_server == TRUE)
is_default = TRUE;
if (serv->ServerPort) {
if (!SocketBindTight) {
#ifdef PR_USE_IPV6
if (ipv6_supported) {
pr_inet_set_default_family(NULL, AF_INET6);
} else {
pr_inet_set_default_family(NULL, pr_netaddr_get_family(serv->addr));
}
#else
pr_inet_set_default_family(NULL, pr_netaddr_get_family(serv->addr));
#endif /* PR_USE_IPV6 */
}
serv->listen = pr_inet_create_connection(serv->pool, server_list, -1,
(SocketBindTight ? serv->addr : NULL), serv->ServerPort, FALSE);
PR_CREATE_IPBIND(serv, serv->addr);
PR_OPEN_IPBIND(serv->addr, serv->ServerPort, serv->listen, is_default,
FALSE, TRUE);
PR_ADD_IPBINDS(serv);
} else if (is_default) {
serv->listen = NULL;
PR_CREATE_IPBIND(serv, serv->addr);
PR_OPEN_IPBIND(serv->addr, serv->ServerPort, serv->listen, is_default,
FALSE, TRUE);
PR_ADD_IPBINDS(serv);
} else
serv->listen = NULL;
} else {
/* 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.
*/
is_default = FALSE;
if ((default_server = get_param_ptr(serv->conf, "DefaultServer",
FALSE)) != NULL && *default_server == TRUE)
is_default = TRUE;
serv->listen = main_server->listen;
register_cleanup(serv->listen->pool, &serv->listen, server_cleanup_cb,
server_cleanup_cb);
PR_CREATE_IPBIND(serv, serv->addr);
PR_OPEN_IPBIND(serv->addr, serv->ServerPort, NULL, is_default, FALSE,
TRUE);
PR_ADD_IPBINDS(serv);
}
}
/* done */
return;
}
void init_bindings(void) {
#ifdef PR_USE_IPV6
int sock;
/* Check to see whether we can actually create an IPv6 socket. */
sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) {
ipv6_supported = FALSE;
} else {
close(sock);
}
#endif /* PR_USE_IPV6 */
if (ServerType == SERVER_INETD)
init_inetd_bindings();
else if (ServerType == SERVER_STANDALONE)
init_standalone_bindings();
return;
}
Last Updated: Thu Feb 23 11:07:10 2006
HTML generated by tj's src2html script