root/usr/src/cmd/nscd/nscd_frontend.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright 2012 Milan Jurik. All rights reserved.
 * Copyright 2018 Joyent, Inc.
 * Copyright 2019 Nexenta Systems, Inc.
 */

#include <stdlib.h>
#include <alloca.h>
#include <signal.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <door.h>
#include <zone.h>
#include <resolv.h>
#include <sys/socket.h>
#include <net/route.h>
#include <string.h>
#include <net/if.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "nscd_common.h"
#include "nscd_door.h"
#include "nscd_config.h"
#include "nscd_switch.h"
#include "nscd_log.h"
#include "nscd_selfcred.h"
#include "nscd_frontend.h"
#include "nscd_admin.h"

static void rts_mon(void);
static void keep_open_dns_socket(void);

extern nsc_ctx_t *cache_ctx_p[];

/*
 * Current active Configuration data for the frontend component
 */
static nscd_cfg_global_frontend_t       frontend_cfg_g;
static nscd_cfg_frontend_t              *frontend_cfg;

static int      max_servers = 0;
static int      max_servers_set = 0;
static int      per_user_is_on = 1;

static char     *main_execname;
static char     **main_argv;
extern int      _whoami;
extern long     activity;
extern mutex_t  activity_lock;

static sema_t   common_sema;

static thread_key_t     lookup_state_key;
static mutex_t          create_lock = DEFAULTMUTEX;
static int              num_servers = 0;
static thread_key_t     server_key;

/*
 * Bind a TSD value to a server thread. This enables the destructor to
 * be called if/when this thread exits.  This would be a programming
 * error, but better safe than sorry.
 */
/*ARGSUSED*/
static void *
server_tsd_bind(void *arg)
{
        static void *value = "NON-NULL TSD";

        (void) thr_setname(thr_self(), "server_tsd_bind");

        /* disable cancellation to avoid hangs if server threads disappear */
        (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
        (void) thr_setspecific(server_key, value);
        (void) door_return(NULL, 0, NULL, 0);

        /* make lint happy */
        return (NULL);
}

/*
 * Server threads are created here.
 */
/*ARGSUSED*/
static void
server_create(door_info_t *dip)
{
        (void) mutex_lock(&create_lock);
        if (++num_servers > max_servers) {
                num_servers--;
                (void) mutex_unlock(&create_lock);
                return;
        }
        (void) mutex_unlock(&create_lock);
        (void) thr_create(NULL, 0, server_tsd_bind, NULL,
            THR_BOUND|THR_DETACHED, NULL);
}

/*
 * Server thread are destroyed here
 */
/*ARGSUSED*/
static void
server_destroy(void *arg)
{
        (void) mutex_lock(&create_lock);
        num_servers--;
        (void) mutex_unlock(&create_lock);
        (void) thr_setspecific(server_key, NULL);
}

/*
 * get clearance
 */
int
_nscd_get_clearance(sema_t *sema)
{
        if (sema_trywait(&common_sema) == 0) {
                (void) thr_setspecific(lookup_state_key, NULL);
                return (0);
        }

        if (sema_trywait(sema) == 0) {
                (void) thr_setspecific(lookup_state_key, (void*)1);
                return (0);
        }

        return (1);
}


/*
 * release clearance
 */
int
_nscd_release_clearance(sema_t *sema)
{
        int     which;

        (void) thr_getspecific(lookup_state_key, (void**)&which);
        if (which == 0) /* from common pool */ {
                (void) sema_post(&common_sema);
                return (0);
        }

        (void) sema_post(sema);
        return (1);
}

static void
dozip(int signal __unused)
{
        /* not much here */
}

/*
 * _nscd_restart_if_cfgfile_changed()
 * Restart if modification times of nsswitch.conf or resolv.conf have changed.
 *
 * If nsswitch.conf has changed then it is possible that sources for
 * various backends have changed and therefore the current cached
 * data may not be consistent with the new data sources.  By
 * restarting the cache will be cleared and the new configuration will
 * be used.
 *
 * The check for resolv.conf is made as only the first call to
 * res_gethostbyname() or res_getaddrbyname() causes a call to
 * res_ninit() to occur which in turn parses resolv.conf.  Therefore
 * to benefit from changes to resolv.conf nscd must be restarted when
 * resolv.conf is updated, removed or created.  If res_getXbyY calls
 * are removed from NSS then this check could be removed.
 *
 */
void
_nscd_restart_if_cfgfile_changed()
{

        static mutex_t          nsswitch_lock = DEFAULTMUTEX;
        static timestruc_t      last_nsswitch_check = { 0 };
        static timestruc_t      last_nsswitch_modified = { 0 };
        static timestruc_t      last_resolv_modified = { -1, 0 };
        static mutex_t          restarting_lock = DEFAULTMUTEX;
        static int              restarting = 0;
        int                     restart = 0;
        time_t                  now = time(NULL);
        char                    *me = "_nscd_restart_if_cfgfile_changed";

#define FLAG_RESTART_REQUIRED   if (restarting == 0) {\
                                        (void) mutex_lock(&restarting_lock);\
                                        if (restarting == 0) {\
                                                restarting = 1;\
                                                restart = 1;\
                                        }\
                                        (void) mutex_unlock(&restarting_lock);\
                                }

        if (restarting == 1)
                return;

        if (now - last_nsswitch_check.tv_sec < _NSC_FILE_CHECK_TIME)
                return;

        (void) mutex_lock(&nsswitch_lock);

        if (now - last_nsswitch_check.tv_sec >= _NSC_FILE_CHECK_TIME) {
                struct stat nss_buf;
                struct stat res_buf;

                last_nsswitch_check.tv_sec = now;
                last_nsswitch_check.tv_nsec = 0;

                (void) mutex_unlock(&nsswitch_lock); /* let others continue */

                if (stat("/etc/nsswitch.conf", &nss_buf) < 0) {
                        return;
                } else if (last_nsswitch_modified.tv_sec == 0) {
                        last_nsswitch_modified = nss_buf.st_mtim;
                }

                if (last_nsswitch_modified.tv_sec < nss_buf.st_mtim.tv_sec ||
                    (last_nsswitch_modified.tv_sec == nss_buf.st_mtim.tv_sec &&
                    last_nsswitch_modified.tv_nsec < nss_buf.st_mtim.tv_nsec)) {
                        FLAG_RESTART_REQUIRED;
                }

                if (restart == 0) {
                        if (stat("/etc/resolv.conf", &res_buf) < 0) {
                                /* Unable to stat file, were we previously? */
                                if (last_resolv_modified.tv_sec > 0) {
                                        /* Yes, it must have been removed. */
                                        FLAG_RESTART_REQUIRED;
                                } else if (last_resolv_modified.tv_sec == -1) {
                                        /* No, then we've never seen it. */
                                        last_resolv_modified.tv_sec = 0;
                                }
                        } else if (last_resolv_modified.tv_sec == -1) {
                                /* We've just started and file is present. */
                                last_resolv_modified = res_buf.st_mtim;
                        } else if (last_resolv_modified.tv_sec == 0) {
                                /* Wasn't there at start-up. */
                                FLAG_RESTART_REQUIRED;
                        } else if (last_resolv_modified.tv_sec <
                            res_buf.st_mtim.tv_sec ||
                            (last_resolv_modified.tv_sec ==
                            res_buf.st_mtim.tv_sec &&
                            last_resolv_modified.tv_nsec <
                            res_buf.st_mtim.tv_nsec)) {
                                FLAG_RESTART_REQUIRED;
                        }
                }

                if (restart == 1) {
                        char *fmri;

                        /*
                         * if in self cred mode, kill the forker and
                         * child nscds
                         */
                        if (_nscd_is_self_cred_on(0, NULL)) {
                                _nscd_kill_forker();
                                _nscd_kill_all_children();
                        }

                        /*
                         * time for restart
                         */
                        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO)
                        (me, "nscd restart due to %s or %s change\n",
                            "/etc/nsswitch.conf", "resolv.conf");
                        /*
                         * try to restart under smf
                         */
                        if ((fmri = getenv("SMF_FMRI")) == NULL) {
                                /* not running under smf - reexec */
                                (void) execv(main_execname, main_argv);
                                exit(1); /* just in case */
                        }

                        if (smf_restart_instance(fmri) == 0)
                                (void) sleep(10); /* wait a bit */
                        exit(1); /* give up waiting for resurrection */
                }

        } else
                (void) mutex_unlock(&nsswitch_lock);
}

uid_t
_nscd_get_client_euid()
{
        ucred_t *uc = NULL;
        uid_t   id;
        char    *me = "get_client_euid";

        if (door_ucred(&uc) != 0) {
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "door_ucred: %s\n", strerror(errno));
                return ((uid_t)-1);
        }

        id = ucred_geteuid(uc);
        ucred_free(uc);
        return (id);
}

/*
 * Check to see if the door client's euid is 0 or if it has required_priv
 * privilege. Return 0 if yes, -1 otherwise.
 * Supported values for required_priv are:
 *    - NSCD_ALL_PRIV: for all zones privileges
 *    - NSCD_READ_PRIV: for PRIV_FILE_DAC_READ privilege
 */
int
_nscd_check_client_priv(int required_priv)
{
        int                     rc = 0;
        ucred_t                 *uc = NULL;
        const priv_set_t        *eset;
        char                    *me = "_nscd_check_client_read_priv";
        priv_set_t              *zs;    /* zone */

        if (door_ucred(&uc) != 0) {
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                (me, "door_ucred: %s\n", strerror(errno));
                return (-1);
        }

        if (ucred_geteuid(uc) == 0) {
                ucred_free(uc);
                return (0);
        }

        eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
        switch (required_priv) {
                case NSCD_ALL_PRIV:
                        zs = priv_str_to_set("zone", ",", NULL);
                        if (!priv_isequalset(eset, zs)) {
                                _NSCD_LOG(NSCD_LOG_FRONT_END,
                                    NSCD_LOG_LEVEL_ERROR)
                                (me, "missing all zones privileges\n");
                                rc = -1;
                        }
                        priv_freeset(zs);
                        break;
                case NSCD_READ_PRIV:
                        if (!priv_ismember(eset, PRIV_FILE_DAC_READ))
                                rc = -1;
                        break;
                default:
                        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                        (me, "unknown required_priv: %d\n", required_priv);
                        rc = -1;
                        break;
        }
        ucred_free(uc);
        return (rc);
}

static void
N2N_check_priv(
        void                    *buf,
        char                    *dc_str)
{
        nss_pheader_t           *phdr = (nss_pheader_t *)buf;
        ucred_t                 *uc = NULL;
        const priv_set_t        *eset;
        zoneid_t                zoneid;
        int                     errnum;
        char                    *me = "N2N_check_priv";

        if (door_ucred(&uc) != 0) {
                errnum = errno;
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "door_ucred: %s\n", strerror(errno));

                NSCD_SET_STATUS(phdr, NSS_ERROR, errnum);
                return;
        }

        eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
        zoneid = ucred_getzoneid(uc);

        if ((zoneid != GLOBAL_ZONEID && zoneid != getzoneid()) ||
            eset != NULL ? !priv_ismember(eset, PRIV_SYS_ADMIN) :
            ucred_geteuid(uc) != 0) {

                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
                (me, "%s call failed(cred): caller pid %d, uid %d, "
                    "euid %d, zoneid %d\n", dc_str, ucred_getpid(uc),
                    ucred_getruid(uc), ucred_geteuid(uc), zoneid);
                ucred_free(uc);

                NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
                return;
        }

        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
        (me, "nscd received %s cmd from pid %d, uid %d, "
            "euid %d, zoneid %d\n", dc_str, ucred_getpid(uc),
            ucred_getruid(uc), ucred_geteuid(uc), zoneid);

        ucred_free(uc);

        NSCD_SET_STATUS_SUCCESS(phdr);
}

void
_nscd_APP_check_cred(
        void            *buf,
        pid_t           *pidp,
        char            *dc_str,
        int             log_comp,
        int             log_level)
{
        nss_pheader_t   *phdr = (nss_pheader_t *)buf;
        ucred_t         *uc = NULL;
        uid_t           ruid;
        uid_t           euid;
        pid_t           pid;
        int             errnum;
        char            *me = "_nscd_APP_check_cred";

        if (door_ucred(&uc) != 0) {
                errnum = errno;
                _NSCD_LOG(log_comp, NSCD_LOG_LEVEL_ERROR)
                (me, "door_ucred: %s\n", strerror(errno));

                NSCD_SET_STATUS(phdr, NSS_ERROR, errnum);
                return;
        }

        NSCD_SET_STATUS_SUCCESS(phdr);
        pid = ucred_getpid(uc);
        if (NSS_PACKED_CRED_CHECK(buf, ruid = ucred_getruid(uc),
            euid = ucred_geteuid(uc))) {
                if (pidp != NULL) {
                        if (*pidp == (pid_t)-1)
                                *pidp = pid;
                        else if (*pidp != pid) {
                                NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
                        }
                }
        } else {
                NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
        }

        ucred_free(uc);

        if (NSCD_STATUS_IS_NOT_OK(phdr)) {
                _NSCD_LOG(log_comp, log_level)
                (me, "%s call failed: caller pid %d (input pid = %d), ruid %d, "
                    "euid %d, header ruid %d, header euid %d\n", dc_str,
                    pid, (pidp != NULL) ? *pidp : -1, ruid, euid,
                    ((nss_pheader_t *)(buf))->p_ruid,
                    ((nss_pheader_t *)(buf))->p_euid);
        }
}

/* log error and return -1 when an invalid packed buffer header is found */
static int
pheader_error(nss_pheader_t *phdr, uint32_t call_number)
{
        char *call_num_str;

        switch (call_number) {
        case NSCD_SEARCH:
                call_num_str = "NSCD_SEARCH";
                break;
        case NSCD_SETENT:
                call_num_str = "NSCD_SETENT";
                break;
        case NSCD_GETENT:
                call_num_str = "NSCD_GETENT";
                break;
        case NSCD_ENDENT:
                call_num_str = "NSCD_ENDENT";
                break;
        case NSCD_PUT:
                call_num_str = "NSCD_PUT";
                break;
        case NSCD_GETHINTS:
                call_num_str = "NSCD_GETHINTS";
                break;
        default:
                call_num_str = "UNKNOWN";
                break;
        }

        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
        ("pheader_error", "call number %s: invalid packed buffer header\n",
            call_num_str);

        NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
        return (-1);
}

/*
 * Validate the header of a getXbyY or setent/getent/endent request.
 * Return 0 if good, -1 otherwise.
 *
 * A valid header looks like the following (size is arg_size, does
 * not include the output area):
 * +----------------------------------+ --
 * | nss_pheader_t (header fixed part)| ^
 * |                                  | |
 * | pbufsiz, dbd,off, key_off,       | len = sizeof(nss_pheader_t)
 * | data_off ....                    | |
 * |                                  | v
 * +----------------------------------+ <----- dbd_off
 * | dbd (database description)       | ^
 * | nss_dbd_t + up to 3 strings      | |
 * | length = sizeof(nss_dbd_t) +     | len = key_off - dbd_off
 * |          length of 3 strings +   | |
 * |          length of padding       | |
 * | (total length in multiple of 4)  | v
 * +----------------------------------+ <----- key_off
 * | lookup key                       | ^
 * | nss_XbyY_key_t, content varies,  | |
 * | based on database and lookup op  | len = data_off - key_off
 * | length = data_off - key_off      | |
 * | including padding, multiple of 4 | v
 * +----------------------------------+ <----- data_off (= arg_size)
 * |                                  | ^
 * | area to hold results             | |
 * |                                  | len = data_len (= pbufsiz -
 * |                                  | |                 data_off)
 * |                                  | v
 * +----------------------------------+ <----- pbufsiz
 */
static int
validate_pheader(
        void            *argp,
        size_t          arg_size,
        uint32_t        call_number)
{
        nss_pheader_t   *phdr = (nss_pheader_t *)(void *)argp;
        nssuint_t       l1, l2;

        /*
         * current version is NSCD_HEADER_REV, length of the fixed part
         * of the header must match the size of nss_pheader_t
         */
        if (phdr->p_version != NSCD_HEADER_REV ||
            phdr->dbd_off != sizeof (nss_pheader_t))
                return (pheader_error(phdr, call_number));

        /*
         * buffer size and offsets must be in multiple of 4
         */
        if ((arg_size & 3) || (phdr->dbd_off & 3) || (phdr->key_off & 3) ||
            (phdr->data_off & 3))
                return (pheader_error(phdr, call_number));

        /*
         * the input arg_size is the length of the request header
         * and should be less than NSCD_PHDR_MAXLEN
         */
        if (phdr->data_off != arg_size || arg_size > NSCD_PHDR_MAXLEN)
                return (pheader_error(phdr, call_number));

        /* get length of the dbd area */
        l1 = phdr->key_off - phdr-> dbd_off;

        /*
         * dbd area may contain padding, so length of dbd should
         * not be less than the length of the actual data
         */
        if (l1 < phdr->dbd_len)
                return (pheader_error(phdr, call_number));

        /* get length of the key area */
        l2 = phdr->data_off - phdr->key_off;

        /*
         * key area may contain padding, so length of key area should
         * not be less than the length of the actual data
         */
        if (l2 < phdr->key_len)
                return (pheader_error(phdr, call_number));

        /*
         * length of fixed part + lengths of dbd and key area = length of
         * the request header
         */
        if (sizeof (nss_pheader_t) + l1 + l2 != phdr->data_off)
                return (pheader_error(phdr, call_number));

        /* header length + data length = buffer length */
        if (phdr->data_off + phdr->data_len != phdr->pbufsiz)
                return (pheader_error(phdr, call_number));

        return (0);
}

/* log error and return -1 when an invalid nscd to nscd buffer is found */
static int
N2Nbuf_error(nss_pheader_t *phdr, uint32_t call_number)
{
        char *call_num_str;

        switch (call_number) {
        case NSCD_PING:
                call_num_str = "NSCD_PING";
                break;

        case NSCD_IMHERE:
                call_num_str = "NSCD_IMHERE";
                break;

        case NSCD_PULSE:
                call_num_str = "NSCD_PULSE";
                break;

        case NSCD_FORK:
                call_num_str = "NSCD_FORK";
                break;

        case NSCD_KILL:
                call_num_str = "NSCD_KILL";
                break;

        case NSCD_REFRESH:
                call_num_str = "NSCD_REFRESH";
                break;

        case NSCD_GETPUADMIN:
                call_num_str = "NSCD_GETPUADMIN";
                break;

        case NSCD_GETADMIN:
                call_num_str = "NSCD_GETADMIN";
                break;

        case NSCD_SETADMIN:
                call_num_str = "NSCD_SETADMIN";
                break;

        case NSCD_KILLSERVER:
                call_num_str = "NSCD_KILLSERVER";
                break;
        default:
                call_num_str = "UNKNOWN";
                break;
        }

        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
        ("N2Nbuf_error", "call number %s: invalid N2N buffer\n", call_num_str);

        NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0,
            NSCD_DOOR_BUFFER_CHECK_FAILED);

        return (-1);
}

/*
 * Validate the buffer of an nscd to nscd request.
 * Return 0 if good, -1 otherwise.
 *
 * A valid buffer looks like the following (size is arg_size):
 * +----------------------------------+ --
 * | nss_pheader_t (header fixed part)| ^
 * |                                  | |
 * | pbufsiz, dbd,off, key_off,       | len = sizeof(nss_pheader_t)
 * | data_off ....                    | |
 * |                                  | v
 * +----------------------------------+ <---dbd_off = key_off = data_off
 * |                                  | ^
 * | input data/output data           | |
 * | OR no data                       | len = data_len (= pbufsiz -
 * |                                  | |                 data_off)
 * |                                  | | len could be zero
 * |                                  | v
 * +----------------------------------+ <--- pbufsiz
 */
static int
validate_N2Nbuf(
        void            *argp,
        size_t          arg_size,
        uint32_t        call_number)
{
        nss_pheader_t   *phdr = (nss_pheader_t *)(void *)argp;

        /*
         * current version is NSCD_HEADER_REV, length of the fixed part
         * of the header must match the size of nss_pheader_t
         */
        if (phdr->p_version != NSCD_HEADER_REV ||
            phdr->dbd_off != sizeof (nss_pheader_t))
                return (N2Nbuf_error(phdr, call_number));

        /*
         * There are no dbd and key data, so the dbd, key, data
         * offsets should be equal
         */
        if (phdr->dbd_off != phdr->key_off ||
            phdr->dbd_off != phdr->data_off)
                return (N2Nbuf_error(phdr, call_number));

        /*
         * the input arg_size is the buffer length and should
         * be less or equal than NSCD_N2NBUF_MAXLEN
         */
        if (phdr->pbufsiz != arg_size || arg_size > NSCD_N2NBUF_MAXLEN)
                return (N2Nbuf_error(phdr, call_number));

        /* header length + data length = buffer length */
        if (phdr->data_off + phdr->data_len != phdr->pbufsiz)
                return (N2Nbuf_error(phdr, call_number));

        return (0);
}

static void
lookup(char *argp, size_t arg_size)
{
        nsc_lookup_args_t       largs;
        char                    space[NSCD_LOOKUP_BUFSIZE];
        nss_pheader_t           *phdr = (nss_pheader_t *)(void *)argp;

        NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space,
            sizeof (space));

        /*
         * make sure the first couple bytes of the data area is null,
         * so that bad strings in the packed header stop here
         */
        (void) memset((char *)phdr + phdr->data_off, 0, 16);

        (void) memset(&largs, 0, sizeof (largs));
        largs.buffer = argp;
        largs.bufsize = arg_size;
        nsc_lookup(&largs, 0);

        /*
         * only the PUN needs to keep track of the
         * activity count to determine when to
         * terminate itself
         */
        if (_whoami == NSCD_CHILD) {
                (void) mutex_lock(&activity_lock);
                ++activity;
                (void) mutex_unlock(&activity_lock);
        }

        NSCD_SET_RETURN_ARG(phdr, arg_size);
        (void) door_return(argp, arg_size, NULL, 0);
}

static void
getent(char *argp, size_t arg_size)
{
        char                    space[NSCD_LOOKUP_BUFSIZE];
        nss_pheader_t           *phdr = (nss_pheader_t *)(void *)argp;

        NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space, sizeof (space));

        nss_pgetent(argp, arg_size);

        NSCD_SET_RETURN_ARG(phdr, arg_size);
        (void) door_return(argp, arg_size, NULL, 0);
}

static int
is_db_per_user(void *buf, char *dblist)
{
        nss_pheader_t   *phdr = (nss_pheader_t *)buf;
        nss_dbd_t       *pdbd;
        char            *dbname, *dbn;
        int             len;

        /* copy db name into a temp buffer */
        pdbd = (nss_dbd_t *)((void *)((char *)buf + phdr->dbd_off));
        dbname = (char *)pdbd + pdbd->o_name;
        len = strlen(dbname);
        dbn = alloca(len + 2);
        (void) memcpy(dbn, dbname, len);

        /* check if <dbname> + ',' can be found in the dblist string */
        dbn[len] = ',';
        dbn[len + 1] = '\0';
        if (strstr(dblist, dbn) != NULL)
                return (1);

        /*
         * check if <dbname> can be found in the last part
         * of the dblist string
         */
        dbn[len] = '\0';
        if (strstr(dblist, dbn) != NULL)
                return (1);

        return (0);
}

/*
 * Check to see if all conditions are met for processing per-user
 * requests. Returns 1 if yes, -1 if backend is not configured,
 * 0 otherwise.
 */
static int
need_per_user_door(void *buf, int whoami, uid_t uid, char **dblist)
{
        nss_pheader_t   *phdr = (nss_pheader_t *)buf;

        NSCD_SET_STATUS_SUCCESS(phdr);

        /* if already a per-user nscd, no need to get per-user door */
        if (whoami == NSCD_CHILD)
                return (0);

        /* forker shouldn't be asked */
        if (whoami == NSCD_FORKER) {
                NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
                return (0);
        }

        /* if door client is root, no need for a per-user door */
        if (uid == 0)
                return (0);

        /*
         * if per-user lookup is not configured, no per-user
         * door available
         */
        if (_nscd_is_self_cred_on(0, dblist) == 0)
                return (-1);

        /*
         * if per-user lookup is not configured for the db,
         * don't bother
         */
        if (is_db_per_user(phdr, *dblist) == 0)
                return (0);

        return (1);
}

static void
if_selfcred_return_per_user_door(char *argp, size_t arg_size,
    door_desc_t *dp, int whoami)
{
        nss_pheader_t   *phdr = (nss_pheader_t *)((void *)argp);
        char            *dblist;
        int             door = -1;
        int             rc = 0;
        door_desc_t     desc;
        char            *space;
        int             len;

        /*
         * check to see if self-cred is configured and
         * need to return an alternate PUN door
         */
        if (per_user_is_on == 1) {
                rc = need_per_user_door(argp, whoami,
                    _nscd_get_client_euid(), &dblist);
                if (rc == -1)
                        per_user_is_on = 0;
        }
        if (rc <= 0) {
                /*
                 * self-cred not configured, and no error detected,
                 * return to continue the door call processing
                 */
                if (NSCD_STATUS_IS_OK(phdr))
                        return;
                else
                        /*
                         * configured but error detected,
                         * stop the door call processing
                         */
                        (void) door_return(argp, phdr->data_off, NULL, 0);
        }

        /* get the alternate PUN door */
        _nscd_proc_alt_get(argp, &door);
        if (NSCD_GET_STATUS(phdr) != NSS_ALTRETRY) {
                (void) door_return(argp, phdr->data_off, NULL, 0);
        }

        /* return the alternate door descriptor */
        len = strlen(dblist) + 1;
        space = alloca(arg_size + len);
        phdr->data_len = len;
        (void) memcpy(space, phdr, arg_size);
        (void) strncpy((char *)space + arg_size, dblist, len);
        dp = &desc;
        dp->d_attributes = DOOR_DESCRIPTOR;
        dp->d_data.d_desc.d_descriptor = door;
        arg_size += len;
        (void) door_return(space, arg_size, dp, 1);
}

/*ARGSUSED*/
static void
switcher(void *cookie, char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc)
{
        int                     iam;
        pid_t                   ent_pid = -1;
        nss_pheader_t           *phdr = (nss_pheader_t *)((void *)argp);
        void                    *uptr;
        int                     len;
        size_t                  buflen;
        int                     callnum;
        char                    *me = "switcher";

        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
        (me, "switcher ...\n");

        if (argp == DOOR_UNREF_DATA) {
                (void) printf("Door Slam... exiting\n");
                exit(0);
        }

        if (argp == NULL) { /* empty door call */
                (void) door_return(NULL, 0, 0, 0); /* return the favor */
        }

        /*
         *  need to restart if main nscd and config file(s) changed
         */
        if (_whoami == NSCD_MAIN)
                _nscd_restart_if_cfgfile_changed();

        if ((phdr->nsc_callnumber & NSCDV2CATMASK) == NSCD_CALLCAT_APP) {

                /* make sure the packed buffer header is good */
                if (validate_pheader(argp, arg_size,
                    phdr->nsc_callnumber) == -1)
                        (void) door_return(argp, arg_size, NULL, 0);

                switch (phdr->nsc_callnumber) {

                case NSCD_SEARCH:

                /* if a fallback to main nscd, skip per-user setup */
                if (phdr->p_status != NSS_ALTRETRY)
                        if_selfcred_return_per_user_door(argp, arg_size,
                            dp, _whoami);
                lookup(argp, arg_size);

                break;

                case NSCD_SETENT:

                _nscd_APP_check_cred(argp, &ent_pid, "NSCD_SETENT",
                    NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT);
                if (NSCD_STATUS_IS_OK(phdr)) {
                        if_selfcred_return_per_user_door(argp, arg_size,
                            dp, _whoami);
                        nss_psetent(argp, arg_size, ent_pid);
                }
                break;

                case NSCD_GETENT:

                getent(argp, arg_size);
                break;

                case NSCD_ENDENT:

                nss_pendent(argp, arg_size);
                break;

                case NSCD_PUT:

                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                (me, "door call NSCD_PUT not supported yet\n");

                NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
                break;

                case NSCD_GETHINTS:

                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                (me, "door call NSCD_GETHINTS not supported yet\n");

                NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
                break;

                default:

                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                (me, "Unknown name service door call op %x\n",
                    phdr->nsc_callnumber);

                NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
                break;
                }

                (void) door_return(argp, arg_size, NULL, 0);
        }

        iam = NSCD_MAIN;
        callnum = phdr->nsc_callnumber & ~NSCD_WHOAMI;
        if (callnum == NSCD_IMHERE ||
            callnum == NSCD_PULSE || callnum == NSCD_FORK)
                iam = phdr->nsc_callnumber & NSCD_WHOAMI;
        else
                callnum = phdr->nsc_callnumber;

        /* nscd -> nscd v2 calls */

        /* make sure the buffer is good */
        if (validate_N2Nbuf(argp, arg_size, callnum) == -1)
                (void) door_return(argp, arg_size, NULL, 0);

        switch (callnum) {

        case NSCD_PING:
                NSCD_SET_STATUS_SUCCESS(phdr);
                break;

        case NSCD_IMHERE:
                _nscd_proc_iamhere(argp, dp, n_desc, iam);
                break;

        case NSCD_PULSE:
                N2N_check_priv(argp, "NSCD_PULSE");
                if (NSCD_STATUS_IS_OK(phdr))
                        _nscd_proc_pulse(argp, iam);
                break;

        case NSCD_FORK:
                N2N_check_priv(argp, "NSCD_FORK");
                if (NSCD_STATUS_IS_OK(phdr))
                        _nscd_proc_fork(argp, iam);
                break;

        case NSCD_KILL:
                N2N_check_priv(argp, "NSCD_KILL");
                if (NSCD_STATUS_IS_OK(phdr))
                        exit(0);
                break;

        case NSCD_REFRESH:
                N2N_check_priv(argp, "NSCD_REFRESH");
                if (NSCD_STATUS_IS_OK(phdr)) {
                        if (_nscd_refresh() != NSCD_SUCCESS)
                                exit(1);
                        NSCD_SET_STATUS_SUCCESS(phdr);
                }
                break;

        case NSCD_GETPUADMIN:

                if (_nscd_is_self_cred_on(0, NULL)) {
                        _nscd_peruser_getadmin(argp, sizeof (nscd_admin_t));
                } else {
                        NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0,
                            NSCD_SELF_CRED_NOT_CONFIGURED);
                }
                break;

        case NSCD_GETADMIN:

                len = _nscd_door_getadmin((void *)argp);
                if (len == 0)
                        break;

                /* size of door buffer not big enough, allocate one */
                NSCD_ALLOC_DOORBUF(NSCD_GETADMIN, len, uptr, buflen);

                /* copy packed header */
                *(nss_pheader_t *)uptr = *(nss_pheader_t *)((void *)argp);

                /* set new buffer size */
                ((nss_pheader_t *)uptr)->pbufsiz = buflen;

                /* try one more time */
                (void) _nscd_door_getadmin((void *)uptr);
                (void) door_return(uptr, buflen, NULL, 0);
                break;

        case NSCD_SETADMIN:
                N2N_check_priv(argp, "NSCD_SETADMIN");
                if (NSCD_STATUS_IS_OK(phdr))
                        _nscd_door_setadmin(argp);
                break;

        case NSCD_KILLSERVER:
                N2N_check_priv(argp, "NSCD_KILLSERVER");
                if (NSCD_STATUS_IS_OK(phdr)) {
                        /* also kill the forker nscd if one is running */
                        _nscd_kill_forker();
                        exit(0);
                }
                break;

        default:
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                (me, "Unknown name service door call op %d\n",
                    phdr->nsc_callnumber);

                NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);

                (void) door_return(argp, arg_size, NULL, 0);
                break;

        }
        (void) door_return(argp, arg_size, NULL, 0);
}

int
_nscd_setup_server(char *execname, char **argv)
{

        int             fd;
        int             errnum;
        int             bind_failed = 0;
        mode_t          old_mask;
        struct stat     buf;
        sigset_t        myset;
        struct sigaction action;
        char            *me = "_nscd_setup_server";

        main_execname = execname;
        main_argv = argv;

        /* Any nscd process is to ignore SIGPIPE */
        if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
                errnum = errno;
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                (me, "signal (SIGPIPE): %s\n", strerror(errnum));
                return (-1);
        }

        keep_open_dns_socket();

        /*
         * the max number of server threads should be fixed now, so
         * set flag to indicate that no in-flight change is allowed
         */
        max_servers_set = 1;

        (void) thr_keycreate(&lookup_state_key, NULL);
        (void) sema_init(&common_sema, frontend_cfg_g.common_worker_threads,
            USYNC_THREAD, 0);

        /* Establish server thread pool */
        (void) door_server_create(server_create);
        if (thr_keycreate(&server_key, server_destroy) != 0) {
                errnum = errno;
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                (me, "thr_keycreate (server thread): %s\n",
                    strerror(errnum));
                return (-1);
        }

        /* Create a door */
        if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
            DOOR_UNREF | DOOR_NO_CANCEL)) < 0) {
                errnum = errno;
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                (me, "door_create: %s\n", strerror(errnum));
                return (-1);
        }

        /* if not main nscd, no more setup to do */
        if (_whoami != NSCD_MAIN)
                return (fd);

        /* bind to file system */
        if (is_system_labeled() && (getzoneid() == GLOBAL_ZONEID)) {
                if (stat(TSOL_NAME_SERVICE_DOOR, &buf) < 0) {
                        int     newfd;

                        /* make sure the door will be readable by all */
                        old_mask = umask(0);
                        if ((newfd = creat(TSOL_NAME_SERVICE_DOOR, 0444)) < 0) {
                                errnum = errno;
                                _NSCD_LOG(NSCD_LOG_FRONT_END,
                                    NSCD_LOG_LEVEL_ERROR)
                                (me, "Cannot create %s: %s\n",
                                    TSOL_NAME_SERVICE_DOOR,
                                    strerror(errnum));
                                bind_failed = 1;
                        }
                        /* rstore the old file mode creation mask */
                        (void) umask(old_mask);
                        (void) close(newfd);
                }
                if (symlink(TSOL_NAME_SERVICE_DOOR, NAME_SERVICE_DOOR) != 0) {
                        if (errno != EEXIST) {
                                errnum = errno;
                                _NSCD_LOG(NSCD_LOG_FRONT_END,
                                    NSCD_LOG_LEVEL_ERROR)
                                (me, "Cannot symlink %s: %s\n",
                                    NAME_SERVICE_DOOR, strerror(errnum));
                                bind_failed = 1;
                        }
                }
        } else if (stat(NAME_SERVICE_DOOR, &buf) < 0) {
                int     newfd;

                /* make sure the door will be readable by all */
                old_mask = umask(0);
                if ((newfd = creat(NAME_SERVICE_DOOR, 0444)) < 0) {
                        errnum = errno;
                        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                        (me, "Cannot create %s: %s\n", NAME_SERVICE_DOOR,
                            strerror(errnum));
                        bind_failed = 1;
                }
                /* rstore the old file mode creation mask */
                (void) umask(old_mask);
                (void) close(newfd);
        }

        if (bind_failed == 1) {
                (void) door_revoke(fd);
                return (-1);
        }

        if (fattach(fd, NAME_SERVICE_DOOR) < 0) {
                if ((errno != EBUSY) ||
                    (fdetach(NAME_SERVICE_DOOR) <  0) ||
                    (fattach(fd, NAME_SERVICE_DOOR) < 0)) {
                        errnum = errno;
                        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                        (me, "fattach: %s\n", strerror(errnum));
                        (void) door_revoke(fd);
                        return (-1);
                }
        }

        /*
         * kick off routing socket monitor thread
         */
        if (thr_create(NULL, 0,
            (void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
                errnum = errno;
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                (me, "thr_create (routing socket monitor): %s\n",
                    strerror(errnum));

                (void) door_revoke(fd);
                return (-1);
        }

        /*
         * set up signal handler for SIGHUP
         */
        action.sa_handler = dozip;
        action.sa_flags = 0;
        (void) sigemptyset(&action.sa_mask);
        (void) sigemptyset(&myset);
        (void) sigaddset(&myset, SIGHUP);

        if (sigaction(SIGHUP, &action, NULL) < 0) {
                errnum = errno;
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                (me, "sigaction (SIGHUP): %s\n", strerror(errnum));

                (void) door_revoke(fd);
                return (-1);
        }

        return (fd);
}

int
_nscd_setup_child_server(int did)
{

        int             errnum;
        int             fd;
        nscd_rc_t       rc;
        char            *me = "_nscd_setup_child_server";

        /* Re-establish our own server thread pool */
        (void) door_server_create(server_create);
        if (thr_keycreate(&server_key, server_destroy) != 0) {
                errnum = errno;
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "thr_keycreate failed: %s", strerror(errnum));
                return (-1);
        }

        /*
         * Create a new door.
         * Keep DOOR_REFUSE_DESC (self-cred nscds don't fork)
         */
        (void) close(did);
        if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
            DOOR_REFUSE_DESC|DOOR_UNREF|DOOR_NO_CANCEL)) < 0) {
                errnum = errno;
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
                (me, "door_create failed: %s", strerror(errnum));
                return (-1);
        }

        /*
         * kick off routing socket monitor thread
         */
        if (thr_create(NULL, 0,
            (void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
                errnum = errno;
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                (me, "thr_create (routing socket monitor): %s\n",
                    strerror(errnum));
                (void) door_revoke(fd);
                return (-1);
        }

        /*
         * start monitoring the states of the name service clients
         */
        rc = _nscd_init_smf_monitor();
        if (rc != NSCD_SUCCESS) {
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
        (me, "unable to start the SMF monitor (rc = %d)\n", rc);

                (void) door_revoke(fd);
                return (-1);
        }

        return (fd);
}

nscd_rc_t
_nscd_alloc_frontend_cfg()
{
        frontend_cfg  = calloc(NSCD_NUM_DB, sizeof (nscd_cfg_frontend_t));
        if (frontend_cfg == NULL)
                return (NSCD_NO_MEMORY);

        return (NSCD_SUCCESS);
}


/* ARGSUSED */
nscd_rc_t
_nscd_cfg_frontend_notify(
        void                            *data,
        struct nscd_cfg_param_desc      *pdesc,
        nscd_cfg_id_t                   *nswdb,
        nscd_cfg_flag_t                 dflag,
        nscd_cfg_error_t                **errorp,
        void                            *cookie)
{
        void                            *dp;

        /*
         * At init time, the whole group of config params are received.
         * At update time, group or individual parameter value could
         * be received.
         */

        if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_INIT) ||
            _nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) {
                /*
                 * group data is received, copy in the
                 * entire strcture
                 */
                if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL))
                        frontend_cfg_g = *(nscd_cfg_global_frontend_t *)data;
                else
                        frontend_cfg[nswdb->index] =
                            *(nscd_cfg_frontend_t *)data;

        } else {
                /*
                 * individual paramater is received: copy in the
                 * parameter value.
                 */
                if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL))
                        dp = (char *)&frontend_cfg_g + pdesc->p_offset;
                else
                        dp = (char *)&frontend_cfg[nswdb->index] +
                            pdesc->p_offset;
                (void) memcpy(dp, data, pdesc->p_size);
        }

        return (NSCD_SUCCESS);
}

/* ARGSUSED */
nscd_rc_t
_nscd_cfg_frontend_verify(
        void                            *data,
        struct  nscd_cfg_param_desc     *pdesc,
        nscd_cfg_id_t                   *nswdb,
        nscd_cfg_flag_t                 dflag,
        nscd_cfg_error_t                **errorp,
        void                            **cookie)
{

        char                            *me = "_nscd_cfg_frontend_verify";

        /*
         * if max. number of server threads is set and in effect,
         * don't allow changing of the frontend configuration
         */
        if (max_servers_set) {
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO)
        (me, "changing of the frontend configuration not allowed now");

                return (NSCD_CFG_CHANGE_NOT_ALLOWED);
        }

        return (NSCD_SUCCESS);
}

/* ARGSUSED */
nscd_rc_t
_nscd_cfg_frontend_get_stat(
        void                            **stat,
        struct nscd_cfg_stat_desc       *sdesc,
        nscd_cfg_id_t                   *nswdb,
        nscd_cfg_flag_t                 *dflag,
        void                            (**free_stat)(void *stat),
        nscd_cfg_error_t                **errorp)
{
        return (NSCD_SUCCESS);
}

void
_nscd_init_cache_sema(sema_t *sema, char *cache_name)
{
        int     i, j;
        char    *dbn;

        if (max_servers == 0)
                max_servers = frontend_cfg_g.common_worker_threads +
                    frontend_cfg_g.cache_hit_threads;

        for (i = 0; i < NSCD_NUM_DB; i++) {

                dbn = NSCD_NSW_DB_NAME(i);
                if (strcasecmp(dbn, cache_name) == 0) {
                        j = frontend_cfg[i].worker_thread_per_nsw_db;
                        (void) sema_init(sema, j, USYNC_THREAD, 0);
                        max_servers += j;
                        break;
                }
        }
}

/*
 * Monitor the routing socket.  Address lists stored in the ipnodes
 * cache are sorted based on destination address selection rules,
 * so when things change that could affect that sorting (interfaces
 * go up or down, flags change, etc.), we clear that cache so the
 * list will be re-ordered the next time the hostname is resolved.
 */
static void
rts_mon(void)
{
        int     rt_sock, rdlen, idx;
        union {
                struct {
                        struct rt_msghdr rtm;
                        struct sockaddr_storage addrs[RTA_NUMBITS];
                } r;
                struct if_msghdr ifm;
                struct ifa_msghdr ifam;
        } mbuf;
        struct ifa_msghdr *ifam = &mbuf.ifam;
        char    *me = "rts_mon";

        (void) thr_setname(thr_self(), me);

        rt_sock = socket(PF_ROUTE, SOCK_RAW, 0);
        if (rt_sock < 0) {
                _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                (me, "Failed to open routing socket: %s\n", strerror(errno));
                thr_exit(0);
        }

        for (;;) {
                rdlen = read(rt_sock, &mbuf, sizeof (mbuf));
                if (rdlen <= 0) {
                        if (rdlen == 0 || (errno != EINTR && errno != EAGAIN)) {
                                _NSCD_LOG(NSCD_LOG_FRONT_END,
                                    NSCD_LOG_LEVEL_ERROR)
                                (me, "routing socket read: %s\n",
                                    strerror(errno));
                                thr_exit(0);
                        }
                        continue;
                }
                if (ifam->ifam_version != RTM_VERSION) {
                                _NSCD_LOG(NSCD_LOG_FRONT_END,
                                    NSCD_LOG_LEVEL_ERROR)
                                (me, "rx unknown version (%d) on "
                                    "routing socket.\n",
                                    ifam->ifam_version);
                        continue;
                }
                switch (ifam->ifam_type) {
                case RTM_NEWADDR:
                case RTM_DELADDR:
                        /* if no ipnodes cache, then nothing to do */
                        idx = get_cache_idx("ipnodes");
                        if (cache_ctx_p[idx] == NULL ||
                            cache_ctx_p[idx]->reaper_on != nscd_true)
                                break;
                        nsc_invalidate(cache_ctx_p[idx], NULL, NULL);
                        break;
                case RTM_ADD:
                case RTM_DELETE:
                case RTM_CHANGE:
                case RTM_GET:
                case RTM_LOSING:
                case RTM_REDIRECT:
                case RTM_MISS:
                case RTM_LOCK:
                case RTM_OLDADD:
                case RTM_OLDDEL:
                case RTM_RESOLVE:
                case RTM_IFINFO:
                case RTM_CHGADDR:
                case RTM_FREEADDR:
                        break;
                default:
                        _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
                        (me, "rx unknown msg type (%d) on routing socket.\n",
                            ifam->ifam_type);
                        break;
                }
        }
}

static void
keep_open_dns_socket(void)
{
        _res.options |= RES_STAYOPEN; /* just keep this udp socket open */
}