root/usr/src/lib/libsldap/common/ns_common.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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
 */


#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <libintl.h>
#include <ctype.h>

#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdir.h>
#include <lber.h>
#include <ldap.h>

#include "ns_sldap.h"
#include "ns_internal.h"
#include "ns_cache_door.h"

#define UDP     "/dev/udp"
#define MAXIFS  32

struct ifinfo {
        struct in_addr addr, netmask;
};

static ns_service_map ns_def_map[] = {
        { "passwd",     "ou=people,",           NULL },
        { "shadow",     "ou=people,",           "passwd" },
        { "user_attr",  "ou=people,",           "passwd" },
        { "audit_user", "ou=people,",           "passwd" },
        { "group",      "ou=group,",            NULL },
        { "rpc",        "ou=rpc,",              NULL },
        { "project",    "ou=projects,",         NULL },
        { "protocols",  "ou=protocols,",        NULL },
        { "networks",   "ou=networks,",         NULL },
        { "netmasks",   "ou=networks,",         "networks" },
        { "netgroup",   "ou=netgroup,",         NULL },
        { "aliases",    "ou=aliases,",          NULL },
        { "Hosts",      "ou=Hosts,",            NULL },
        { "ipnodes",    "ou=Hosts,",            "hosts" },
        { "Services",   "ou=Services,",         NULL },
        { "bootparams", "ou=ethers,",           "ethers" },
        { "ethers",     "ou=ethers,",           NULL },
        { "auth_attr",  "ou=SolarisAuthAttr,",  NULL },
        { "prof_attr",  "ou=SolarisProfAttr,",  NULL },
        { "exec_attr",  "ou=SolarisProfAttr,",  "prof_attr" },
        { "profile",    "ou=profile,",          NULL },
        { "printers",   "ou=printers,",         NULL },
        { "automount",  "",                     NULL },
        { "tnrhtp",     "ou=ipTnet,",           NULL },
        { "tnrhdb",     "ou=ipTnet,",           "tnrhtp" },
        { NULL, NULL, NULL }
};


static char ** parseDN(const char *val, const char *service);
static char ** sortServerNet(char **srvlist);
static char ** sortServerPref(char **srvlist, char **preflist,
                boolean_t flag, int version, int *error);

/*
 * FUNCTION:    s_api_printResult
 *      Given a ns_ldap_result structure print it.
 */
int
__s_api_printResult(ns_ldap_result_t *result)
{

        ns_ldap_entry_t *curEntry;
        int             i, j, k = 0;

#ifdef DEBUG
        (void) fprintf(stderr, "__s_api_printResult START\n");
#endif
        (void) printf("--------------------------------------\n");
        if (result == NULL) {
                (void) printf("No result\n");
                return (0);
        }
        (void) printf("entries_count %d\n", result->entries_count);
        curEntry = result->entry;
        for (i = 0; i < result->entries_count; i++) {

                (void) printf("entry %d has attr_count = %d \n", i,
                    curEntry->attr_count);
                for (j = 0; j < curEntry->attr_count; j++) {
                        (void) printf("entry %d has attr_pair[%d] = %s \n",
                            i, j, curEntry->attr_pair[j]->attrname);
                        for (k = 0; k < 20 &&
                            curEntry->attr_pair[j]->attrvalue[k]; k++)
                                (void) printf("entry %d has attr_pair[%d]->"
                                    "attrvalue[%d] = %s \n", i, j, k,
                                    curEntry->attr_pair[j]->attrvalue[k]);
                }
                (void) printf("\n--------------------------------------\n");
                curEntry = curEntry->next;
        }
        return (1);
}

/*
 * FUNCTION:    __s_api_getSearchScope
 *
 *      Retrieve the search scope for ldap search from the config module.
 *
 * RETURN VALUES:       NS_LDAP_SUCCESS, NS_LDAP_CONFIG
 * INPUT:               NONE
 * OUTPUT:              searchScope, errorp
 */
int
__s_api_getSearchScope(
        int *searchScope,
        ns_ldap_error_t **errorp)
{

        char            errmsg[MAXERROR];
        void            **paramVal = NULL;
        int             rc = 0;
        int             scope = 0;

#ifdef DEBUG
        (void) fprintf(stderr, "__s_api_getSearchScope START\n");
#endif
        if (*searchScope == 0) {
                if ((rc = __ns_ldap_getParam(NS_LDAP_SEARCH_SCOPE_P,
                    &paramVal, errorp)) != NS_LDAP_SUCCESS) {
                        return (rc);
                }
                if (paramVal && *paramVal)
                        scope = * (int *)(*paramVal);
                else
                        scope = NS_LDAP_SCOPE_ONELEVEL;
                (void) __ns_ldap_freeParam(&paramVal);
        } else {
                scope = *searchScope;
        }

        switch (scope) {

                case    NS_LDAP_SCOPE_ONELEVEL:
                        *searchScope = LDAP_SCOPE_ONELEVEL;
                        break;
                case    NS_LDAP_SCOPE_BASE:
                        *searchScope = LDAP_SCOPE_BASE;
                        break;
                case    NS_LDAP_SCOPE_SUBTREE:
                        *searchScope = LDAP_SCOPE_SUBTREE;
                        break;
                default:
                        (void) snprintf(errmsg, sizeof (errmsg),
                            gettext("Invalid search scope!"));
                        MKERROR(LOG_ERR, *errorp, NS_CONFIG_FILE,
                            strdup(errmsg), NS_LDAP_CONFIG);
                        return (NS_LDAP_CONFIG);
        }

        return (NS_LDAP_SUCCESS);
}

/*
 * FUNCTION:    __ns_ldap_dupAuth
 *
 *      Duplicates an authentication structure.
 *
 * RETURN VALUES:       copy of authp or NULL on error
 * INPUT:               authp
 */
ns_cred_t *
__ns_ldap_dupAuth(const ns_cred_t *authp)
{
        ns_cred_t *ap;

#ifdef DEBUG
        (void) fprintf(stderr, "__ns_ldap_dupAuth START\n");
#endif
        if (authp == NULL)
                return (NULL);

        ap = (ns_cred_t *)calloc(1, sizeof (ns_cred_t));
        if (ap == NULL)
                return (NULL);

        if (authp->hostcertpath) {
                ap->hostcertpath = strdup(authp->hostcertpath);
                if (ap->hostcertpath == NULL) {
                        free(ap);
                        return (NULL);
                }
        }
        if (authp->cred.unix_cred.userID) {
                ap->cred.unix_cred.userID =
                    strdup(authp->cred.unix_cred.userID);
                if (ap->cred.unix_cred.userID == NULL) {
                        (void) __ns_ldap_freeCred(&ap);
                        return (NULL);
                }
        }
        if (authp->cred.unix_cred.passwd) {
                ap->cred.unix_cred.passwd =
                    strdup(authp->cred.unix_cred.passwd);
                if (ap->cred.unix_cred.passwd == NULL) {
                        (void) __ns_ldap_freeCred(&ap);
                        return (NULL);
                }
        }
        if (authp->cred.cert_cred.nickname) {
                ap->cred.cert_cred.nickname =
                    strdup(authp->cred.cert_cred.nickname);
                if (ap->cred.cert_cred.nickname == NULL) {
                        (void) __ns_ldap_freeCred(&ap);
                        return (NULL);
                }
        }
        ap->auth.type = authp->auth.type;
        ap->auth.tlstype = authp->auth.tlstype;
        ap->auth.saslmech = authp->auth.saslmech;
        ap->auth.saslopt = authp->auth.saslopt;
        return (ap);
}

/*
 * FUNCTION:    __ns_ldap_freeUnixCred
 *
 *      Frees all the memory associated with a UnixCred_t structure.
 *
 * RETURN VALUES:       NS_LDAP_INVALID_PARAM, NS_LDAP_SUCCESS
 * INPUT:               UnixCred
 */
int
__ns_ldap_freeUnixCred(UnixCred_t ** credp)
{
        UnixCred_t *ap;

#ifdef DEBUG
        (void) fprintf(stderr, "__ns_ldap_freeUnixCred START\n");
#endif
        if (credp == NULL || *credp == NULL)
                return (NS_LDAP_INVALID_PARAM);

        ap = *credp;
        if (ap->userID) {
                (void) memset(ap->userID, 0, strlen(ap->userID));
                free(ap->userID);
        }

        if (ap->passwd) {
                (void) memset(ap->passwd, 0, strlen(ap->passwd));
                free(ap->passwd);
        }

        free(ap);
        *credp = NULL;
        return (NS_LDAP_SUCCESS);
}

/*
 * FUNCTION:    __ns_ldap_freeCred
 *
 *      Frees all the memory associated with a ns_cred_t structure.
 *
 * RETURN VALUES:       NS_LDAP_INVALID_PARAM, NS_LDAP_SUCCESS
 * INPUT:               ns_cred_t
 */
int
__ns_ldap_freeCred(ns_cred_t ** credp)
{
        ns_cred_t *ap;

#ifdef DEBUG
        (void) fprintf(stderr, "__ns_ldap_freeCred START\n");
#endif
        if (credp == NULL || *credp == NULL)
                return (NS_LDAP_INVALID_PARAM);

        ap = *credp;
        if (ap->hostcertpath) {
                (void) memset(ap->hostcertpath, 0,
                    strlen(ap->hostcertpath));
                free(ap->hostcertpath);
        }

        if (ap->cred.unix_cred.userID) {
                (void) memset(ap->cred.unix_cred.userID, 0,
                    strlen(ap->cred.unix_cred.userID));
                free(ap->cred.unix_cred.userID);
        }

        if (ap->cred.unix_cred.passwd) {
                (void) memset(ap->cred.unix_cred.passwd, 0,
                    strlen(ap->cred.unix_cred.passwd));
                free(ap->cred.unix_cred.passwd);
        }

        if (ap->cred.cert_cred.nickname) {
                (void) memset(ap->cred.cert_cred.nickname, 0,
                    strlen(ap->cred.cert_cred.nickname));
                free(ap->cred.cert_cred.nickname);
        }

        free(ap);
        *credp = NULL;
        return (NS_LDAP_SUCCESS);
}

/*
 * FUNCTION:    __s_api_is_auth_matched
 *
 *      Compare an authentication structure.
 *
 * RETURN VALUES:       B_TRUE if matched, B_FALSE otherwise.
 * INPUT:               auth1, auth2
 */
boolean_t
__s_api_is_auth_matched(const ns_cred_t *auth1,
    const ns_cred_t *auth2)
{
        if ((auth1->auth.type != auth2->auth.type) ||
            (auth1->auth.tlstype != auth2->auth.tlstype) ||
            (auth1->auth.saslmech != auth2->auth.saslmech) ||
            (auth1->auth.saslopt != auth2->auth.saslopt))
                return (B_FALSE);

        if ((((auth1->auth.type == NS_LDAP_AUTH_SASL) &&
            ((auth1->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
            (auth1->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
            (auth1->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
            ((auth1->cred.unix_cred.userID == NULL) ||
            (auth1->cred.unix_cred.passwd == NULL) ||
            ((strcasecmp(auth1->cred.unix_cred.userID,
            auth2->cred.unix_cred.userID) != 0)) ||
            ((strcmp(auth1->cred.unix_cred.passwd,
            auth2->cred.unix_cred.passwd) != 0))))
                return (B_FALSE);

        return (B_TRUE);
}

/*
 * FUNCTION:    __s_api_getDNs
 *
 *      Retrieves the default base dn for the given
 *      service.
 *
 * RETURN VALUES:       NS_LDAP_SUCCESS, NS_LDAP_MEMORY, NS_LDAP_CONFIG
 * INPUT:               service
 * OUTPUT:              DN, error
 */
typedef int (*pf)(const char *, char **, ns_ldap_error_t **);
int
__s_api_getDNs(
        char *** DN,
        const char *service,
        ns_ldap_error_t ** error)
{

        void    **paramVal = NULL;
        char    **dns = NULL;
        int     rc = 0;
        int     i, len;
        pf      prepend_auto2dn = __s_api_prepend_automountmapname_to_dn;

#ifdef DEBUG
        (void) fprintf(stderr, "__s_api_getDNs START\n");
#endif
        if ((rc = __ns_ldap_getParam(NS_LDAP_SEARCH_BASEDN_P,
            &paramVal, error)) != NS_LDAP_SUCCESS) {
                return (rc);
        }
        if (!paramVal) {
                char errmsg[MAXERROR];

                (void) snprintf(errmsg, sizeof (errmsg),
                    gettext("BaseDN not defined"));
                MKERROR(LOG_ERR, *error, NS_CONFIG_FILE, strdup(errmsg),
                    NS_LDAP_CONFIG);
                return (NS_LDAP_CONFIG);
        }

        dns = (char **)calloc(2, sizeof (char *));
        if (dns == NULL) {
                (void) __ns_ldap_freeParam(&paramVal);
                return (NS_LDAP_MEMORY);
        }

        if (service == NULL) {
                dns[0] = strdup((char *)*paramVal);
                if (dns[0] == NULL) {
                        (void) __ns_ldap_freeParam(&paramVal);
                        free(dns);
                        return (NS_LDAP_MEMORY);
                }
        } else {
                for (i = 0; ns_def_map[i].service != NULL; i++) {
                        if (strcasecmp(service,
                            ns_def_map[i].service) == 0) {

                                len = strlen((char *)*paramVal) +
                                    strlen(ns_def_map[i].rdn) + 1;
                                dns[0] = (char *)
                                    calloc(len, sizeof (char));
                                if (dns[0] == NULL) {
                                        (void) __ns_ldap_freeParam(
                                            &paramVal);
                                        free(dns);
                                        return (NS_LDAP_MEMORY);
                                }
                                (void) strcpy(dns[0],
                                    ns_def_map[i].rdn);
                                (void) strcat(dns[0],
                                    (char *)*paramVal);
                                break;
                        }
                }
                if (ns_def_map[i].service == NULL) {
                        char *p = (char *)*paramVal;
                        char *buffer = NULL;
                        int  buflen = 0;

                        if (strchr(service, '=') == NULL) {
                            /* automount entries */
                                if (strncasecmp(service, "auto_", 5) == 0) {
                                        buffer = strdup(p);
                                        if (!buffer) {
                                                free(dns);
                                                (void) __ns_ldap_freeParam(
                                                    &paramVal);
                                                return (NS_LDAP_MEMORY);
                                        }
                                        /* shorten name to avoid cstyle error */
                                        rc = prepend_auto2dn(
                                            service, &buffer, error);
                                        if (rc != NS_LDAP_SUCCESS) {
                                                free(dns);
                                                free(buffer);
                                                (void) __ns_ldap_freeParam(
                                                    &paramVal);
                                                return (rc);
                                        }
                                } else {
                                /* strlen("nisMapName")+"="+","+'\0' = 13 */
                                        buflen = strlen(service) + strlen(p) +
                                            13;
                                        buffer = (char *)malloc(buflen);
                                        if (buffer == NULL) {
                                                free(dns);
                                                (void) __ns_ldap_freeParam(
                                                    &paramVal);
                                                return (NS_LDAP_MEMORY);
                                        }
                                        (void) snprintf(buffer, buflen,
                                            "nisMapName=%s,%s", service, p);
                                }
                        } else {
                                buflen = strlen(service) + strlen(p) + 2;
                                buffer = (char *)malloc(buflen);
                                if (buffer == NULL) {
                                        free(dns);
                                        (void) __ns_ldap_freeParam(&paramVal);
                                        return (NS_LDAP_MEMORY);
                                }
                                (void) snprintf(buffer, buflen,
                                    "%s,%s", service, p);
                        }
                        dns[0] = buffer;
                }
        }

        (void) __ns_ldap_freeParam(&paramVal);
        *DN = dns;
        return (NS_LDAP_SUCCESS);
}
/*
 * FUNCTION:    __s_api_get_search_DNs_v1
 *
 *      Retrieves the list of search DNS from the V1 profile for the given
 *      service.
 *
 * RETURN VALUES:       NS_LDAP_SUCCESS, NS_LDAP_MEMORY, NS_LDAP_CONFIG
 * INPUT:               service
 * OUTPUT:              DN, error
 */
int
__s_api_get_search_DNs_v1(
        char *** DN,
        const char *service,
        ns_ldap_error_t ** error)
{

        void    **paramVal = NULL;
        void    **temptr = NULL;
        char    **dns = NULL;
        int     rc = 0;

        if ((rc = __ns_ldap_getParam(NS_LDAP_SEARCH_DN_P,
            &paramVal, error)) != NS_LDAP_SUCCESS) {
                return (rc);
        }

        if (service && paramVal) {
                for (temptr = paramVal; *temptr != NULL; temptr++) {
                        dns = parseDN((const char *)(*temptr),
                            (const char *)service);
                        if (dns != NULL)
                                break;
                }
        }

        (void) __ns_ldap_freeParam(&paramVal);
        *DN = dns;
        return (NS_LDAP_SUCCESS);

}
/*
 * FUNCTION:    parseDN
 *
 *      Parse a special formated list(val) into an array of char *.
 *
 * RETURN VALUE:        A char * pointer to the new list of dns.
 * INPUT:               val, service
 */
static char **
parseDN(
        const char *val,
        const char *service)
{

        size_t          len = 0;
        size_t          slen = 0;
        char            **retVal = NULL;
        const char      *temptr;
        char            *temptr2;
        const char      *valend;
        int             valNo = 0;
        int             valSize = 0;
        int             i;
        char            *SSD_service = NULL;

#ifdef DEBUG
        (void) fprintf(stderr, "parseDN START\n");
#endif
        if (val == NULL || *val == '\0')
                return (NULL);
        if (service == NULL || *service == '\0')
                return (NULL);

        len = strlen(val);
        slen = strlen(service);
        if (strncasecmp(val, service, slen) != 0) {
                /*
                 * This routine is only called
                 * to process V1 profile and
                 * for V1 profile, map service
                 * to the corresponding SSD_service
                 * which is associated with a
                 * real container in the LDAP directory
                 * tree, e.g., map "shadow" to
                 * "password". See function
                 * __s_api_get_SSD_from_SSDtoUse_service
                 * for similar service to SSD_service
                 * mapping handling for V2 profile.
                 */
                for (i = 0; ns_def_map[i].service != NULL; i++) {
                        if (ns_def_map[i].SSDtoUse_service &&
                            strcasecmp(service,
                            ns_def_map[i].service) == 0) {
                                SSD_service =
                                    ns_def_map[i].SSDtoUse_service;
                                break;
                        }
                }

                if (SSD_service == NULL)
                        return (NULL);

                slen = strlen(SSD_service);
                if (strncasecmp(val, SSD_service, slen) != 0)
                        return (NULL);
        }

        temptr = val + slen;
        while (*temptr == SPACETOK || *temptr == TABTOK)
                temptr++;
        if (*temptr != COLONTOK)
                return (NULL);

        while (*temptr) {
                temptr2 = strchr(temptr, OPARATOK);
                if (temptr2 == NULL)
                        break;
                temptr2++;
                temptr2 = strchr(temptr2, CPARATOK);
                if (temptr2 == NULL)
                        break;
                valNo++;
                temptr = temptr2+1;
        }

        retVal = (char **)calloc(valNo +1, sizeof (char *));
        if (retVal == NULL)
                return (NULL);

        temptr = val;
        valend = val+len;

        for (i = 0; (i < valNo) && (temptr < valend); i++) {
                temptr = strchr(temptr, OPARATOK);
                if (temptr == NULL) {
                        __s_api_free2dArray(retVal);
                        return (NULL);
                }
                temptr++;
                temptr2 = strchr(temptr, CPARATOK);
                if (temptr2 == NULL) {
                        __s_api_free2dArray(retVal);
                        return (NULL);
                }
                valSize = temptr2 - temptr;

                retVal[i] = (char *)calloc(valSize + 1, sizeof (char));
                if (retVal[i] == NULL) {
                        __s_api_free2dArray(retVal);
                        return (NULL);
                }
                (void) strncpy(retVal[i], temptr, valSize);
                retVal[i][valSize] = '\0';
                temptr = temptr2 + 1;
        }

        return (retVal);
}


/*
 * __s_api_get_local_interfaces
 *
 * Returns a pointer to an array of addresses and netmasks of all interfaces
 * configured on the system.
 *
 * NOTE: This function is very IPv4 centric.
 */
static struct ifinfo *
__s_api_get_local_interfaces()
{
        struct ifconf           ifc;
        struct ifreq            ifreq, *ifr;
        struct ifinfo           *localinfo;
        struct in_addr          netmask;
        struct sockaddr_in      *sin;
        void                    *buf = NULL;
        int                     fd = 0;
        int                     numifs = 0;
        int                     i, n = 0;

        if ((fd = open(UDP, O_RDONLY)) < 0)
                return ((struct ifinfo *)NULL);

        if (ioctl(fd, SIOCGIFNUM, (char *)&numifs) < 0) {
                numifs = MAXIFS;
        }

        buf = malloc(numifs * sizeof (struct ifreq));
        if (buf == NULL) {
                (void) close(fd);
                return ((struct ifinfo *)NULL);
        }
        ifc.ifc_len = numifs * (int)sizeof (struct ifreq);
        ifc.ifc_buf = buf;
        if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0) {
                (void) close(fd);
                free(buf);
                buf = NULL;
                return ((struct ifinfo *)NULL);
        }
        ifr = (struct ifreq *)buf;
        numifs = ifc.ifc_len/(int)sizeof (struct ifreq);
        localinfo = (struct ifinfo *)malloc((numifs + 1) *
            sizeof (struct ifinfo));
        if (localinfo == NULL) {
                (void) close(fd);
                free(buf);
                buf = NULL;
                return ((struct ifinfo *)NULL);
        }

        for (i = 0, n = numifs; n > 0; n--, ifr++) {
                uint_t ifrflags;

                ifreq = *ifr;
                if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifreq) < 0)
                        continue;

                ifrflags = ifreq.ifr_flags;
                if (((ifrflags & IFF_UP) == 0) ||
                    (ifr->ifr_addr.sa_family != AF_INET))
                        continue;

                if (ioctl(fd, SIOCGIFNETMASK, (char *)&ifreq) < 0)
                        continue;
                netmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;

                if (ioctl(fd, SIOCGIFADDR, (char *)&ifreq) < 0)
                        continue;

                sin = (struct sockaddr_in *)&ifreq.ifr_addr;

                localinfo[i].addr = sin->sin_addr;
                localinfo[i].netmask = netmask;
                i++;
        }
        localinfo[i].addr.s_addr = 0;

        free(buf);
        buf = NULL;
        (void) close(fd);
        return (localinfo);
}


/*
 * __s_api_samenet(char *, struct ifinfo *)
 *
 * Returns 1 if address is on the same subnet of the array of addresses
 * passed in.
 *
 * NOTE: This function is only valid for IPv4 addresses.
 */
static int
__s_api_IPv4sameNet(char *addr, struct ifinfo *ifs)
{
        int             answer = 0;

        if (addr && ifs) {
                char            *addr_raw;
                unsigned long   iaddr;
                int             i;

                if ((addr_raw = strdup(addr)) != NULL) {
                        char    *s;

                        /* Remove port number. */
                        if ((s = strchr(addr_raw, ':')) != NULL)
                                *s = '\0';

                        iaddr = inet_addr(addr_raw);

                        /* Loop through interface list to find match. */
                        for (i = 0; ifs[i].addr.s_addr != 0; i++) {
                                if ((iaddr & ifs[i].netmask.s_addr) ==
                                    (ifs[i].addr.s_addr &
                                    ifs[i].netmask.s_addr))
                                        answer++;
                        }
                        free(addr_raw);
                }
        }

        return (answer);
}

/*
 * FUNCTION:    __s_api_getServers
 *
 *      Retrieve a list of ldap servers from the config module.
 *
 * RETURN VALUE:        NS_LDAP_SUCCESS, NS_LDAP_CONFIG, NS_LDAP_MEMORY
 * INPUT:               NONE
 * OUTPUT:              servers, error
 */
int
__s_api_getServers(
                char *** servers,
                ns_ldap_error_t ** error)
{
        void    **paramVal = NULL;
        char    errmsg[MAXERROR];
        char    **sortServers = NULL;
        char    **netservers = NULL;
        int     rc = 0, err = NS_LDAP_CONFIG, version = 1;
        const   char    *str, *str1;

#ifdef DEBUG
        (void) fprintf(stderr, "__s_api_getServers START\n");
#endif
        *servers = NULL;
        /* get profile version number */
        if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P,
            &paramVal, error)) != NS_LDAP_SUCCESS)
                return (rc);

        if (paramVal == NULL || *paramVal == NULL) {
                (void) snprintf(errmsg, sizeof (errmsg),
                    gettext("No file version"));
                MKERROR(LOG_INFO, *error, NS_CONFIG_FILE, strdup(errmsg),
                    NS_LDAP_CONFIG);
                return (NS_LDAP_CONFIG);
        }

        if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0)
                version = 1;
        else if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_2) == 0)
                version = 2;

        (void) __ns_ldap_freeParam(&paramVal);
        paramVal = NULL;

        if ((rc = __ns_ldap_getParam(NS_LDAP_SERVERS_P,
            &paramVal, error)) != NS_LDAP_SUCCESS)
                return (rc);

        /*
         * For version 2, default server list could be
         * empty.
         */
        if ((paramVal == NULL || (char *)*paramVal == NULL) &&
            version == 1) {
                str = NULL_OR_STR(__s_api_get_configname(NS_LDAP_SERVERS_P));
                (void) snprintf(errmsg, sizeof (errmsg),
                    gettext("Unable to retrieve the '%s' list"), str);
                MKERROR(LOG_WARNING, *error, NS_CONFIG_FILE, strdup(errmsg),
                    NS_LDAP_CONFIG);
                return (NS_LDAP_CONFIG);
        }

        /*
         * Get server address(es) and go through them.
         */
        *servers = (char **)paramVal;
        paramVal = NULL;

        /* Sort servers based on network. */
        if (*servers) {
                netservers = sortServerNet(*servers);
                if (netservers) {
                        free(*servers);
                        *servers = netservers;
                } else {
                        return (NS_LDAP_MEMORY);
                }
        }

        /* Get preferred server list and sort servers based on that. */
        if ((rc = __ns_ldap_getParam(NS_LDAP_SERVER_PREF_P,
            &paramVal, error)) != NS_LDAP_SUCCESS) {
                if (*servers)
                        __s_api_free2dArray(*servers);
                *servers = NULL;
                return (rc);
        }

        if (paramVal != NULL) {
                char **prefServers;
                void **val = NULL;

                if ((rc =  __ns_ldap_getParam(NS_LDAP_PREF_ONLY_P,
                    &val, error)) != NS_LDAP_SUCCESS) {
                                if (*servers)
                                        __s_api_free2dArray(*servers);
                                *servers = NULL;
                        (void) __ns_ldap_freeParam(&paramVal);
                        return (rc);
                }

                prefServers = (char **)paramVal;
                paramVal = NULL;
                if (prefServers) {
                        if (val != NULL && (*val) != NULL &&
                            *(int *)val[0] == 1)
                                sortServers = sortServerPref(*servers,
                                    prefServers, B_FALSE, version,
                                    &err);
                        else
                                sortServers = sortServerPref(*servers,
                                    prefServers, B_TRUE, version,
                                    &err);
                        if (sortServers) {
                                if (*servers)
                                        free(*servers);
                                *servers = NULL;
                                free(prefServers);
                                prefServers = NULL;
                                *servers = sortServers;
                        } else {
                                if (*servers)
                                        __s_api_free2dArray(*servers);
                                *servers = NULL;
                                __s_api_free2dArray(prefServers);
                                prefServers = NULL;
                        }
                }
                (void) __ns_ldap_freeParam(&val);
        }
        (void) __ns_ldap_freeParam(&paramVal);

        if (*servers == NULL) {
                if (err == NS_LDAP_CONFIG) {
                str = NULL_OR_STR(__s_api_get_configname(
                    NS_LDAP_SERVERS_P));
                str1 = NULL_OR_STR(__s_api_get_configname(
                    NS_LDAP_SERVER_PREF_P));
                        (void) snprintf(errmsg, sizeof (errmsg),
                            gettext("Unable to generate a new server list "
                            "based on '%s' and/or '%s'"), str, str1);
                        MKERROR(LOG_WARNING, *error, NS_CONFIG_FILE,
                            strdup(errmsg), err);
                        return (err);
                }
                return (NS_LDAP_MEMORY);
        }

        return (NS_LDAP_SUCCESS);

}

/*
 * FUNCTION:    sortServerNet
 *      Sort the serverlist based on the distance from client as long
 *      as the list only contains IPv4 addresses.  Otherwise do nothing.
 */
static char **
sortServerNet(char **srvlist)
{
        int             count = 0;
        int             all = 0;
        int             ipv4only = 1;
        struct ifinfo   *ifs = __s_api_get_local_interfaces();
        char            **tsrvs;
        char            **psrvs, **retsrvs;

        /* Sanity check. */
        if (srvlist == NULL || srvlist[0] == NULL)
                return (NULL);

        /* Count the number of servers to sort. */
        for (count = 0; srvlist[count] != NULL; count++) {
                if (!__s_api_isipv4(srvlist[count]))
                        ipv4only = 0;
        }
        count++;

        /* Make room for the returned list of servers. */
        retsrvs = (char **)calloc(count, sizeof (char *));
        if (retsrvs == NULL) {
                free(ifs);
                ifs = NULL;
                return (NULL);
        }

        retsrvs[count - 1] = NULL;

        /* Make a temporary list of servers. */
        psrvs = (char **)calloc(count, sizeof (char *));
        if (psrvs == NULL) {
                free(ifs);
                ifs = NULL;
                free(retsrvs);
                retsrvs = NULL;
                return (NULL);
        }

        /* Filter servers on the same subnet */
        tsrvs = srvlist;
        while (*tsrvs) {
                if (ipv4only && __s_api_IPv4sameNet(*tsrvs, ifs)) {
                        psrvs[all] = *tsrvs;
                        retsrvs[all++] = *(tsrvs);
                }
                tsrvs++;
        }

        /* Filter remaining servers. */
        tsrvs = srvlist;
        while (*tsrvs) {
                char    **ttsrvs = psrvs;

                while (*ttsrvs) {
                        if (strcmp(*tsrvs, *ttsrvs) == 0)
                                break;
                        ttsrvs++;
                }

                if (*ttsrvs == NULL)
                        retsrvs[all++] = *(tsrvs);
                tsrvs++;
        }

        free(ifs);
        ifs = NULL;
        free(psrvs);
        psrvs = NULL;

        return (retsrvs);
}

/*
 * FUNCTION:    sortServerPref
 *      Sort the serverlist based on the preferred server list.
 *
 * The sorting algorithm works as follows:
 *
 * If version 1, if flag is TRUE, find all the servers in both preflist
 * and srvlist, then append other servers in srvlist to this list
 * and return the list.
 * If flag is FALSE, just return srvlist.
 * srvlist can not be empty.
 *
 * If version 2, append all the servers in srvlist
 * but not in preflist to preflist, and return the merged list.
 * If srvlist is empty, just return preflist.
 * If preflist is empty, just return srvlist.
 */
static char **
sortServerPref(char **srvlist, char **preflist,
                boolean_t flag, int version, int *error)
{
        int             i, scount = 0, pcount = 0;
        int             all = 0, dup = 0;
        char            **tsrvs;
        char            **retsrvs;
        char            **dupsrvs;

        /* Count the number of servers to sort. */
        if (srvlist && srvlist[0])
                for (i = 0; srvlist[i] != NULL; i++)
                        scount++;

        /* Sanity check. */
        if (scount == 0 && version == 1) {
                *error = NS_LDAP_CONFIG;
                return (NULL);
        }

        /* Count the number of preferred servers */
        if (preflist && preflist[0])
                for (i = 0; preflist[i] != NULL; i++)
                        pcount++;

        /* Sanity check. */
        if (scount == 0 && pcount == 0) {
                *error = NS_LDAP_CONFIG;
                return (NULL);
        }

        /* Make room for the returned list of servers */
        retsrvs = (char **)calloc(scount + pcount + 1, sizeof (char *));
        if (retsrvs == NULL) {
                *error = NS_LDAP_MEMORY;
                return (NULL);
        }

        /*
         * if the preferred server list is empty,
         * just return a copy of the server list
         */
        if (pcount == 0) {
                tsrvs = srvlist;
                while (*tsrvs)
                        retsrvs[all++] = *(tsrvs++);
                return (retsrvs);
        }
        all = 0;

        /*
         * if the server list is empty,
         * just return a copy of the preferred server list
         */
        if (scount == 0) {
                tsrvs = preflist;
                while (*tsrvs)
                        retsrvs[all++] = *(tsrvs++);
                return (retsrvs);
        }
        all = 0;

        /* Make room for the servers whose memory needs to be freed */
        dupsrvs = (char **)calloc(scount + pcount + 1, sizeof (char *));
        if (dupsrvs == NULL) {
                free(retsrvs);
                *error = NS_LDAP_MEMORY;
                return (NULL);
        }

        /*
         * If version 1,
         * throw out preferred servers not on server list.
         * If version 2, make a copy of the preferred server list.
         */
        if (version == 1) {
                tsrvs = preflist;
                while (*tsrvs) {
                        char    **ttsrvs = srvlist;

                        while (*ttsrvs) {
                                if (strcmp(*tsrvs, *(ttsrvs)) == 0)
                                        break;
                                ttsrvs++;
                        }
                        if (*ttsrvs != NULL)
                                retsrvs[all++] = *tsrvs;
                        else
                                dupsrvs[dup++] = *tsrvs;
                        tsrvs++;
                }
        } else {
                tsrvs = preflist;
                while (*tsrvs)
                        retsrvs[all++] = *(tsrvs++);
        }
        /*
         * If version 1,
         * if PREF_ONLY is false, we append the non-preferred servers
         * to bottom of list.
         * For version 2, always append.
         */
        if (flag == B_TRUE || version != 1) {

                tsrvs = srvlist;
                while (*tsrvs) {
                        char    **ttsrvs = preflist;

                        while (*ttsrvs) {
                                if (strcmp(*tsrvs, *ttsrvs) == 0) {
                                        break;
                                }
                                ttsrvs++;
                        }
                        if (*ttsrvs == NULL)
                                retsrvs[all++] = *tsrvs;
                        else
                                dupsrvs[dup++] = *tsrvs;
                        tsrvs++;
                }
        }

        /* free memory for duplicate servers */
        if (dup) {
                for (tsrvs = dupsrvs; *tsrvs; tsrvs++)
                        free(*tsrvs);
        }
        free(dupsrvs);

        return (retsrvs);
}

/*
 * FUNCTION:    __s_api_removeBadServers
 *      Contacts the ldap cache manager for marking the
 *      problem servers as down, so that the server is
 *      not contacted until the TTL expires.
 */
void
__s_api_removeBadServers(char ** Servers)
{

        char    **host;

        if (Servers == NULL)
                return;

        for (host = Servers; *host != NULL; host++) {
                if (__s_api_removeServer(*host) < 0) {
                        /*
                         * Couldn't remove server from
                         * server list. Log a warning.
                         */
                        syslog(LOG_WARNING, "libsldap: could "
                            "not remove %s from servers list", *host);
                }
        }
}

/*
 * FUNCTION:    __s_api_free2dArray
 */
void
__s_api_free2dArray(char ** inarray)
{

        char    **temptr;

        if (inarray == NULL)
                return;

        for (temptr = inarray; *temptr != NULL; temptr++) {
                free(*temptr);
        }
        free(inarray);
}

/*
 * FUNCTION:    __s_api_cp2dArray
 */
char **
__s_api_cp2dArray(char **inarray)
{
        char    **newarray;
        char     **ttarray, *ret;
        int     count;

        if (inarray == NULL)
                return (NULL);

        for (count = 0; inarray[count] != NULL; count++)
                ;

        newarray = (char **)calloc(count + 1, sizeof (char *));
        if (newarray == NULL)
                return (NULL);

        ttarray = newarray;
        for (; *inarray; inarray++) {
                *(ttarray++) = ret = strdup(*inarray);
                if (ret == NULL) {
                        __s_api_free2dArray(newarray);
                        return (NULL);
                }
        }
        return (newarray);
}

/*
 * FUNCTION:    __s_api_isCtrlSupported
 *      Determines if the passed control is supported by the LDAP sever.
 * RETURNS:     NS_LDAP_SUCCESS if yes, NS_LDAP_OP_FAIL if not.
 */
int
__s_api_isCtrlSupported(Connection *con, char *ctrlString)
{
        char            **ctrl;
        int             len;

        len = strlen(ctrlString);
        for (ctrl = con->controls; ctrl && *ctrl; ctrl++) {
                if (strncasecmp(*ctrl, ctrlString, len) == 0)
                        return (NS_LDAP_SUCCESS);
        }
        return (NS_LDAP_OP_FAILED);
}

/*
 * FUNCTION:    __s_api_toFollowReferrals
 *      Determines if need to follow referral for an SLDAP API.
 * RETURN VALUES:       NS_LDAP_SUCCESS, NS_LDAP_INVALID_PARAM, or
 *                      other rc from __ns_ldap_getParam()
 * INPUT:               flags
 * OUTPUT:              toFollow, errorp
 */
int
__s_api_toFollowReferrals(const int flags,
        int *toFollow,
        ns_ldap_error_t **errorp)
{
        void            **paramVal = NULL;
        int             rc = 0;
        int             iflags = 0;

#ifdef DEBUG
        (void) fprintf(stderr, "__s_api_toFollowReferrals START\n");
#endif

        /* Either NS_LDAP_NOREF or NS_LDAP_FOLLOWREF not both */
        if ((flags & (NS_LDAP_NOREF | NS_LDAP_FOLLOWREF)) ==
            (NS_LDAP_NOREF | NS_LDAP_FOLLOWREF)) {
                return (NS_LDAP_INVALID_PARAM);
        }

        /*
         * if the NS_LDAP_NOREF or NS_LDAP_FOLLOWREF is set
         * this will take precendence over the values specified
         * in the configuration file
         */
        if (flags & (NS_LDAP_NOREF | NS_LDAP_FOLLOWREF)) {
                        iflags = flags;
        } else {
                rc = __ns_ldap_getParam(NS_LDAP_SEARCH_REF_P,
                    &paramVal, errorp);
                if (rc != NS_LDAP_SUCCESS)
                        return (rc);
                if (paramVal == NULL || *paramVal == NULL) {
                        (void) __ns_ldap_freeParam(&paramVal);
                        if (*errorp)
                                (void) __ns_ldap_freeError(errorp);
                        *toFollow = TRUE;
                        return (NS_LDAP_SUCCESS);
                }
                iflags = (* (int *)(*paramVal));
                (void) __ns_ldap_freeParam(&paramVal);
        }

        if (iflags & NS_LDAP_NOREF)
                *toFollow = FALSE;
        else
                *toFollow = TRUE;

        return (NS_LDAP_SUCCESS);
}

/*
 * FUNCTION:    __s_api_addRefInfo
 *      Insert a referral info into a referral info list.
 * RETURN VALUES:       NS_LDAP_SUCCESS, NS_LDAP_MEMORY, NS_LDAP_OP_FAILED
 * INPUT:               LDAP URL, pointer to the referral info list,
 *                      search baseDN, search scope, search filter,
 *                      previous connection
 */
int
__s_api_addRefInfo(ns_referral_info_t **head, char *url,
                        char *baseDN, int *scope,
                        char *filter, LDAP *ld)
{
        char                    errmsg[MAXERROR], *tmp;
        ns_referral_info_t      *ref, *tmpref;
        LDAPURLDesc             *ludp = NULL;
        int                     hostlen;
        char *ld_defhost = NULL;

#ifdef DEBUG
        (void) fprintf(stderr, "__s_api_addRefInfo START\n");
#endif

        /* sanity check */
        if (head == NULL)
                return (NS_LDAP_OP_FAILED);

        /*
         * log error and return NS_LDAP_SUCCESS
         * if one of the following:
         * 1. non-LDAP URL
         * 2. LDAP URL which can not be parsed
         */
        if (!ldap_is_ldap_url(url) ||
            ldap_url_parse_nodn(url, &ludp) != 0) {
                (void) snprintf(errmsg, MAXERROR, "%s: %s",
                    gettext("Invalid or non-LDAP URL when"
                    " processing referrals URL"),
                    url);
                syslog(LOG_ERR, "libsldap: %s", errmsg);
                if (ludp)
                                ldap_free_urldesc(ludp);
                return (NS_LDAP_SUCCESS);
        }

        ref = (ns_referral_info_t *)calloc(1,
            sizeof (ns_referral_info_t));
        if (ref == NULL) {
                ldap_free_urldesc(ludp);
                return (NS_LDAP_MEMORY);
        }

        /*
         * we do have a valid URL and we were able to parse it
         * however, we still need to find out what hostport to
         * use if none were provided in the LDAP URL
         * (e.g., ldap:///...)
         */
        if ((ludp->lud_port == 0) && (ludp->lud_host == NULL)) {
                if (ld == NULL) {
                        (void) snprintf(errmsg, MAXERROR, "%s: %s",
                            gettext("no LDAP handle when"
                            " processing referrals URL"),
                            url);
                        syslog(LOG_WARNING, "libsldap: %s", errmsg);
                        ldap_free_urldesc(ludp);
                        free(ref);
                        return (NS_LDAP_SUCCESS);
                } else {
                        (void) ldap_get_option(ld, LDAP_OPT_HOST_NAME,
                            &ld_defhost);
                        if (ld_defhost == NULL) {
                                (void) snprintf(errmsg, MAXERROR, "%s: %s",
                                    gettext("not able to retrieve default "
                                    "host when processing "
                                    "referrals URL"),
                                    url);
                                syslog(LOG_WARNING, "libsldap: %s", errmsg);
                                ldap_free_urldesc(ludp);
                                free(ref);
                                return (NS_LDAP_SUCCESS);
                        } else {
                                ref->refHost = strdup(ld_defhost);
                                if (ref->refHost == NULL) {
                                        ldap_free_urldesc(ludp);
                                        free(ref);
                                        return (NS_LDAP_MEMORY);
                                }
                        }
                }
        } else {
                /*
                 * add 4 here:
                 * 1 for the last '\0'.
                 * 1 for host and prot separator ":"
                 * and "[" & "]" for possible ipV6 addressing
                 */
                hostlen = strlen(ludp->lud_host) +
                    sizeof (MAXPORTNUMBER_STR) + 4;
                ref->refHost = (char *)malloc(hostlen);
                if (ref->refHost == NULL) {
                        ldap_free_urldesc(ludp);
                        free(ref);
                        return (NS_LDAP_MEMORY);
                }

                if (ludp->lud_port != 0) {
                        /*
                         * serverAddr = host:port
                         * or
                         * if host is an IPV6 address
                         * [host]:port
                         */
                        tmp = strstr(url, ludp->lud_host);
                        if (tmp && (tmp > url) && *(tmp - 1) == '[') {
                                (void) snprintf(ref->refHost, hostlen,
                                    "[%s]:%d",
                                    ludp->lud_host,
                                    ludp->lud_port);
                        } else {
                                (void) snprintf(ref->refHost, hostlen,
                                    "%s:%d",
                                    ludp->lud_host,
                                    ludp->lud_port);
                        }
                } else {
                        /* serverAddr = host */
                        (void) snprintf(ref->refHost, hostlen, "%s",
                            ludp->lud_host);
                }
        }

        if (ludp->lud_dn) {
                ref->refDN = strdup(ludp->lud_dn);
                if (ref->refDN == NULL) {
                        ldap_free_urldesc(ludp);
                        free(ref->refHost);
                        free(ref);
                        return (NS_LDAP_MEMORY);
                }
        } else {
                if (baseDN) {
                        ref->refDN = strdup(baseDN);
                        if (ref->refDN == NULL) {
                                ldap_free_urldesc(ludp);
                                free(ref->refHost);
                                free(ref);
                                return (NS_LDAP_MEMORY);
                        }
                }
        }

        if (filter)
                ref->refFilter = strdup(filter);
        else if (ludp->lud_filter)
                ref->refFilter = strdup(ludp->lud_filter);
        else
                ref->refFilter = strdup("");

        if (ref->refFilter == NULL) {
                ldap_free_urldesc(ludp);
                free(ref->refHost);
                if (ref->refDN)
                        free(ref->refDN);
                free(ref);
                return (NS_LDAP_MEMORY);
        }

        /*
         * If the scope is specified in the URL use it.
         * Note if the scope is missing in the URL, ldap_url_parse_nodn()
         * returns the scope BASE. We need to check that the scope of BASE
         * is actually present in the URL.
         * If the scope is missing in the URL then use the passed-in
         * scope.
         * If there is no passed-in scope, then use the scope SUBTREE.
         */
        if (ludp->lud_dn && ludp->lud_scope != LDAP_SCOPE_BASE)
                ref->refScope = ludp->lud_scope;
        else if (ludp->lud_dn && strstr(url, "?base"))
                ref->refScope = LDAP_SCOPE_BASE;
        else if (scope)
                ref->refScope = *scope;
        else
                ref->refScope = LDAP_SCOPE_SUBTREE;

        ref->next = NULL;

        ldap_free_urldesc(ludp);

        /* insert the referral info */
        if (*head) {
                for (tmpref = *head; tmpref->next; tmpref = tmpref->next)
                        ;
                tmpref->next = ref;
        } else
                *head = ref;

        return (NS_LDAP_SUCCESS);
}

/*
 * FUNCTION:    __s_api_deleteRefInfo
 *      Delete a referral info list.
 * INPUT:               pointer to the referral info list
 */
void
__s_api_deleteRefInfo(ns_referral_info_t *head)
{
        ns_referral_info_t      *ref, *tmp;

#ifdef DEBUG
        (void) fprintf(stderr, "__s_api_deleteRefInfo START\n");
#endif

        for (ref = head; ref; ) {
                if (ref->refHost)
                        free(ref->refHost);
                if (ref->refDN)
                        free(ref->refDN);
                if (ref->refFilter)
                        free(ref->refFilter);
                tmp = ref->next;
                free(ref);
                ref = tmp;
        }

}

/*
 * FUNCTION:    __s_api_get_SSD_from_SSDtoUse_service
 *
 *      Retrieves the Service Search Descriptors which should be used for
 *      the given service. For example, return all the "passwd" SSDs for
 *      service "shadow" if no SSD is defined for service "shadow" and
 *      no filter component is defined in all the "passwd" SSDs. This idea
 *      of sharing the SSDs defined for some other service is to reduce the
 *      configuration complexity. For a service, which does not have its own
 *      entries in the LDAP directory, SSD for it is useless, and should not
 *      be set. But since this service must share the container with at least
 *      one other service which does have it own entries, the SSD for
 *      this other service will be shared by this service.
 *      This other service is called the SSD-to-use service.
 *      The static data structure, ns_def_map[], in this file
 *      defines the SSD-to-use service for all the services supported.
 *
 * RETURN VALUES:       NS_LDAP_SUCCESS, NS_LDAP_MEMORY, NS_LDAP_INVALID_PARAM
 * INPUT:               service
 * OUTPUT:              *SSDlist, *errorp if error
 */
int
__s_api_get_SSD_from_SSDtoUse_service(const char *service,
                ns_ldap_search_desc_t ***SSDlist,
                ns_ldap_error_t **errorp)
{
        int                     i, rc;
        int                     found = FALSE;
        int                     filter_found = FALSE;
        char                    *SSD_service = NULL;
        char                    errmsg[MAXERROR];
        ns_ldap_search_desc_t   **sdlist;
        int                     auto_service = FALSE;

#ifdef DEBUG
        (void) fprintf(stderr,
            "__s_api_get_SSD_from_SSDtoUse_service START\n");
#endif

        if (SSDlist == NULL || errorp == NULL)
                return (NS_LDAP_INVALID_PARAM);

        *SSDlist = NULL;
        *errorp = NULL;

        if (service == NULL)
                return (NS_LDAP_SUCCESS);

        if (strncasecmp(service, "auto_", 5) == 0)
                auto_service = TRUE;

        /*
         * First try to return the configured SSDs for the input server
         */
        rc = __ns_ldap_getSearchDescriptors(service, SSDlist, errorp);
        if (rc != NS_LDAP_SUCCESS)
                return (rc);
        else {
                if (*SSDlist != NULL)
                        return (NS_LDAP_SUCCESS);
        }

        /*
         * If service == auto_* and SSD is not found,
         * then try automount to see if there is an SSD
         * for automount.
         */

        if (auto_service) {
                rc = __ns_ldap_getSearchDescriptors(
                    "automount", SSDlist, errorp);
                if (rc != NS_LDAP_SUCCESS)
                        return (rc);
                else {
                        if (*SSDlist != NULL) {
                                /*
                                 * If SSDlist is found,
                                 * prepend automountMapName to the basedn
                                 * in the SSDlist
                                 *
                                 */
                                rc = __s_api_prepend_automountmapname(
                                    service,
                                    SSDlist,
                                    errorp);

                                if (rc != NS_LDAP_SUCCESS) {
                                        (void) __ns_ldap_freeSearchDescriptors(
                                            SSDlist);
                                        *SSDlist = NULL;
                                }

                                return (rc);
                        }
                }
        }

        /*
         * Find the SSDtoUse service.
         * If none found, flag "found" remains FALSE.
         */
        for (i = 0; ns_def_map[i].service != NULL; i++) {
                if (ns_def_map[i].SSDtoUse_service &&
                    strcasecmp(service,
                    ns_def_map[i].service) == 0) {
                        found = TRUE;
                        SSD_service = ns_def_map[i].SSDtoUse_service;
                        break;
                }
        }

        if (!found)
                return (NS_LDAP_SUCCESS);

        /*
         * return the SSDs for SSD_service only if no optional filter
         * component is defined in the SSDs
         */
        rc = __ns_ldap_getSearchDescriptors(SSD_service,
            SSDlist, errorp);
        if (rc != NS_LDAP_SUCCESS) {
                return (rc);
        } else {
                if (*SSDlist == NULL)
                        return (NS_LDAP_SUCCESS);

                /* check to see if filter defined in SSD */
                for (sdlist = *SSDlist; *sdlist; sdlist++) {
                        if ((*sdlist)->filter &&
                            strlen((*sdlist)->filter) > 0) {
                                filter_found = TRUE;
                                break;
                        }
                }
                if (filter_found) {
                        (void) __ns_ldap_freeSearchDescriptors(SSDlist);
                        *SSDlist = NULL;
                        (void) snprintf(errmsg, sizeof (errmsg),
                            gettext("Service search descriptor for "
                            "service '%s' contains filter, "
                            "which can not be used for "
                            "service '%s'."),
                            SSD_service, service);
                        MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE,
                            strdup(errmsg), NS_LDAP_CONFIG);
                        return (NS_LDAP_CONFIG);
                }

        }
        return (NS_LDAP_SUCCESS);
}


/*
 * verify addr is an IPv4 address with the optional [:portno]
 * RFC2373 & RFC2732 & RFC2396
 */
int
__s_api_isipv4(char *addr)
{
        int i, seg, digit, port;

        if (!addr)
                return (0);

        digit = seg = port = 0;

        for (i = 0; i < strlen(addr); i++) {
                if (isdigit(addr[i])) {
                        digit++;
                        continue;
                }
                if (addr[i] == '.') {
                        if (digit > 3 || digit == 0)
                                return (0);
                        digit = 0;
                        seg++;
                        continue;
                }
                if (addr[i] == ':') {
                        if (digit > 3)
                                return (0);
                        port++;
                        digit = 0;
                        seg++;
                        continue;
                }
                return (0);
        }

        if ((seg == 3 && port == 0 && digit > 0 && digit < 4) ||
            (seg == 4 && port == 1 && digit > 0))
                return (1);

        return (0);
}


/*
 * verify addr is an IPv6 address with the optional [IPv6]:portno
 * RFC2373 & RFC2732 & RFC2396
 */
int
__s_api_isipv6(char *addr)
{
        int i, col, digit, port, dc, tc;
        char *laddr, *c1, *s;

        if (!addr)
                return (0);

        s = addr;
        laddr = NULL;
        digit = col = port = 0;
        if (addr[0] == '[') {
                laddr = strdup(addr);
                if (!laddr)
                        return (0);
                c1 = strchr(laddr, ']');
                /* only 1 ']' should be in an addr */
                if (!c1 || (strchr(c1+1, ']')))
                        goto bad;
                switch (c1[1]) {
                        case ':':
                                port++;
                                for (i = 2; i < strlen(c1); i++) {
                                        if (!isdigit(c1[i]))
                                                goto bad;
                                        digit++;
                                }
                                if (!digit)
                                        goto bad;
                                c1[0] = '\0';
                                break;
                        case '\0':
                                c1[0] = '\0';
                                break;
                        default:
                                goto bad;
                }
                s = &laddr[1];
        }

        digit = dc = tc = 0;
        for (i = 0; i < strlen(s); i++) {
                if (isxdigit(s[i])) {
                        if (digit == 0)
                                dc = i;
                        digit++;
                        col = 0;
                        continue;
                }
                if (s[i] == ':') {
                        tc++;
                        if ((col > 1) || (i && !col && !digit))
                                goto bad;
                        digit = 0;
                        col++;
                        continue;
                }
                if (s[i] == '.') {
                        if (__s_api_isipv4(&s[dc]) && tc)
                                goto good;
                        else
                                goto bad;
                }
                goto bad;
        }

good:
        free(laddr);
        return (1);
bad:
        free(laddr);
        return (0);
}


/*
 * verify addr is a valid hostname with the optional [:portno]
 * RFC2373 & RFC2732 & RFC2396
 */
int
__s_api_ishost(char *addr)
{
        int i, seg, alpha, digit, port;

        if (!addr)
                return (0);

        alpha = digit = seg = port = 0;

        /* must start with alpha character */
        if (!isalpha(addr[0]))
                return (0);

        for (i = 0; i < strlen(addr); i++) {
                if (isalpha(addr[i]) || (i && addr[i] == '-')) {
                        alpha++;
                        continue;
                }
                if (isdigit(addr[i])) {
                        digit++;
                        continue;
                }
                if (addr[i] == '.') {
                        if (!alpha && !digit)
                                return (0);
                        alpha = digit = 0;
                        seg++;
                        continue;
                }
                if (addr[i] == ':') {
                        if (!alpha && !digit)
                                return (0);
                        alpha = digit = 0;
                        port++;
                        seg++;
                        continue;
                }
                return (0);
        }

        if ((port == 0 && (seg || alpha || digit)) ||
            (port == 1 && alpha == 0 && digit))
                return (1);

        return (0);
}


/*
 * Prepend automountMapName=auto_xxx to the basedn
 * in the SSDlist
 */

int __s_api_prepend_automountmapname(
        const char *service,
        ns_ldap_search_desc_t ***SSDlist,
        ns_ldap_error_t **errorp)
{
        int                     i, rc;
        ns_ldap_search_desc_t   ** ssdlist = NULL;

        if (service == NULL || SSDlist == NULL || *SSDlist == NULL)
                return (NS_LDAP_INVALID_PARAM);

        ssdlist = *SSDlist;

        for (i = 0; ssdlist[i] != NULL; i++) {
                rc = __s_api_prepend_automountmapname_to_dn(
                    service, &ssdlist[i]->basedn, errorp);

                if (rc != NS_LDAP_SUCCESS)
                        return (rc);
        }

        return (NS_LDAP_SUCCESS);
}


/*
 * Prepend automountMapName=auto_xxx to the DN
 * Construct a string of
 * "automountMapName=auto_xxx,dn"
 *
 * If automountMapName is mapped to some other attribute,
 * then use the mapping in the setup.
 *
 * If a version 1 profile is in use, use nisMapName for
 * backward compatibility (i.e. "nisMapName=auto_xxx,dn").
 */

int
__s_api_prepend_automountmapname_to_dn(
        const char *service,
        char **dn,
        ns_ldap_error_t **errorp)
{
        int rc, len_s = 0, len_d = 0, len = 0;
        char *buffer = NULL;
        char *default_automountmapname = "automountMapName";
        char *automountmapname = NULL;
        char **mappedattrs = NULL;
        char errstr[MAXERROR];
        void **paramVal = NULL;

        if (service == NULL || dn == NULL || *dn == NULL)
                return (NS_LDAP_INVALID_PARAM);

        rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P, &paramVal, errorp);
        if (rc != NS_LDAP_SUCCESS || !paramVal || !*paramVal) {
                if (paramVal)
                        (void) __ns_ldap_freeParam(&paramVal);
                return (rc);
        }
        if (strcasecmp(*paramVal, NS_LDAP_VERSION_1) == 0) {
                automountmapname = strdup("nisMapName");
                (void) __ns_ldap_freeParam(&paramVal);
                if (automountmapname == NULL) {
                        return (NS_LDAP_MEMORY);
                }
        } else {
                (void) __ns_ldap_freeParam(&paramVal);

                /* Find mapped attribute name of auto_xxx first */
                mappedattrs = __ns_ldap_getMappedAttributes(
                    service, default_automountmapname);
                /*
                 * if mapped attribute name of auto_xxx is not found,
                 * find the mapped attribute name of automount
                 */

                if (mappedattrs == NULL)
                        mappedattrs = __ns_ldap_getMappedAttributes(
                        "automount", default_automountmapname);

                /*
                 * if mapped attr is not found, use the default automountmapname
                 */

                if (mappedattrs == NULL) {
                        automountmapname = strdup(default_automountmapname);
                        if (automountmapname == NULL)
                                return (NS_LDAP_MEMORY);
                } else {
                        if (mappedattrs[0] != NULL) {
                                /*
                                 * Copy it from the mapped attr list
                                 * Assume it's 1 to 1 mapping
                                 * 1 to n does not make sense
                                 */
                                automountmapname = strdup(mappedattrs[0]);
                                __s_api_free2dArray(mappedattrs);
                                if (automountmapname == NULL) {
                                        return (NS_LDAP_MEMORY);
                                }
                        } else {

                                /*
                                 * automountmapname is mapped to an empty string
                                 */

                                __s_api_free2dArray(mappedattrs);

                                (void) sprintf(errstr,
                                    gettext(
                                    "Attribute automountMapName is "
                                    "mapped to an empty string.\n"));

                                MKERROR(LOG_WARNING, *errorp, NS_CONFIG_SYNTAX,
                                    strdup(errstr), NS_LDAP_MEMORY);

                                return (NS_LDAP_CONFIG);
                        }
                }
        }

        len_s = strlen(service);
        len_d  = strlen(*dn);
        /* automountMapName + "=" + service + "," + dn + '\0' */
        len = strlen(automountmapname) + 1 + len_s + 1 + len_d + 1;
        buffer = (char *)malloc(len);
        if (buffer == NULL) {
                free(automountmapname);
                return (NS_LDAP_MEMORY);
        }

        (void) snprintf(buffer, len, "%s=%s,%s",
            automountmapname, service, *dn);

        buffer[len-1] = '\0';

        free(automountmapname);

        /* free the original dn */
        (void) free(*dn);

        *dn = buffer;

        return (NS_LDAP_SUCCESS);
}

/*
 * Map the LDAP error code and error message from LDAP server
 * to a password status used for password aging/management.
 */
ns_ldap_passwd_status_t
__s_api_set_passwd_status(int errnum, char *errmsg)
{
        syslog(LOG_DEBUG, "libsldap: got LDAP errnum %d & message: %s ", errnum,
            (errmsg != NULL) ? errmsg : "error msg not available");
        if (errmsg) {
                if (errnum ==
                    LDAP_INVALID_CREDENTIALS) {
                        /*
                         * case 1 (Bind):
                         * password expired
                         */
                        if (strstr(errmsg,
                            NS_PWDERR_EXPIRED))
                                return (NS_PASSWD_EXPIRED);
                }

                if (errnum ==
                    LDAP_UNWILLING_TO_PERFORM) {
                        /*
                         * case 1.1 (Bind):
                         * password expired
                         */
                        if (strstr(errmsg,
                            NS_PWDERR_EXPIRED))
                                return (NS_PASSWD_EXPIRED);

                        /*
                         * case 2 (Bind):
                         * Account inactivated
                         */
                        if (strstr(errmsg,
                            NS_PWDERR_ACCT_INACTIVATED))
                                return (NS_PASSWD_EXPIRED);


                        /*
                         * case 3 (Modify passwd):
                         * the user is not allow to change
                         * password; only admin can change it
                         */
                        if (strstr(errmsg,
                            NS_PWDERR_CHANGE_NOT_ALLOW))
                                return (NS_PASSWD_CHANGE_NOT_ALLOWED);
                }

                if (errnum ==
                    LDAP_CONSTRAINT_VIOLATION) {
                        /*
                         * case 4 (Bind):
                         * the user account is locked due to
                         * too many login failures.
                         */
                        if (strstr(errmsg,
                            NS_PWDERR_MAXTRIES))
                                return (NS_PASSWD_RETRY_EXCEEDED);
                        /*
                         * case 5 (Modify passwd):
                         * syntax error: the new password
                         * has length less than defined
                         * minimum
                         * Not true anymore with strong password
                         * policies on LDAP server: errmsg that
                         * contain NS_PWDERR_INVALID_SYNTAX may
                         * have different meanings.
                         * To keep compatibility with older password
                         * policy, check if errmsg is strictly equal
                         * to NS_PWDERR_INVALID_SYNTAX and if yes only,
                         * return NS_PASSWD_TOO_SHORT.
                         */
                        if (strcmp(errmsg,
                            NS_PWDERR_INVALID_SYNTAX) == 0)
                                return (NS_PASSWD_TOO_SHORT);
                        if (strstr(errmsg,
                            NS_PWDERR_INVALID_SYNTAX))
                                return (NS_PASSWD_INVALID_SYNTAX);
                        /*
                         * case 6 (Modify passwd):
                         * trivial password: same value as
                         * that of attribute cn, sn, or uid ...
                         */
                        if (strstr(errmsg,
                            NS_PWDERR_TRIVIAL_PASSWD))
                                return (NS_PASSWD_INVALID_SYNTAX);
                        /*
                         * case 7 (Modify passwd):
                         * re-use one of the old passwords
                         * in history list
                         */
                        if (strstr(errmsg,
                            NS_PWDERR_IN_HISTORY))
                                return (NS_PASSWD_IN_HISTORY);
                        /*
                         * case 8 (Modify passwd):
                         * password not allowed to be
                         * changed yet; within minimum
                         * age
                         */
                        if (strstr(errmsg,
                            NS_PWDERR_WITHIN_MIN_AGE))
                                return (NS_PASSWD_WITHIN_MIN_AGE);
                }

        }

        return (NS_PASSWD_GOOD);
}

/*
 * Determine if the input OID list contains
 * one of the password control OIDs, which are:
 * LDAP_CONTROL_PWEXPIRED: 2.16.840.1.113730.3.4.4
 * LDAP_CONTROL_PWEXPIRING: 2.16.840.1.113730.3.4.5.
 * If yes, return 1, if no, 0.
 */
int
__s_api_contain_passwd_control_oid(char **oids)
{
        char **oid;

        if (oids == NULL)
                return (0);

        for (oid = oids; *oid; oid++) {
                if (strcmp(*oid, LDAP_CONTROL_PWEXPIRED) == 0 ||
                    strcmp(*oid, LDAP_CONTROL_PWEXPIRING) == 0) {
                        return (1);
                }
        }

        return (0);
}

/*
 * Determine if the input OID list contains LDAP V3 password less
 * account management control OID, which is:
 * NS_LDAP_ACCOUNT_USABLE_CONTROL:1.3.6.1.4.1.42.2.27.9.5.8
 * If yes, return 1, if no, 0.
 */
int
__s_api_contain_account_usable_control_oid(char **oids)
{
        char **oid;

        if (oids == NULL)
                return (0);

        for (oid = oids; *oid; oid++) {
                if (strcmp(*oid, NS_LDAP_ACCOUNT_USABLE_CONTROL) == 0) {
                        return (1);
                }
        }

        return (0);
}

/*
 * For some databases in name switch, the name and aliases are saved
 * as "cn". When the "cn" valuse are retrieved, there is no distinction
 * which is  the name and which is(are) aliase(s).
 * This function is to parse RDN and find the value of the "cn" and
 * then find the matching value in "cn" attribute.
 * Also see RFC 2307 section 5.6.
 *
 * Input -
 *  entry:      An LDAP entry
 *  attrptr:    A attribute which value appears in RDN
 *              This should be "cn" for the name switch for now.
 *  case_ignore:    0 Case sensitive comparison on the attribute value
 *                  1 Case insensitive comparison
 *
 * Return -
 *              The value of an attrbute which is used as canonical name
 *              This is read only and the caller should not try to free it.
 *              If it's a NULL, it could be either an RDN parsing error
 *              or RDN value does not match any existing "cn" values.
 *              e.g.
 *              dn: cn=xx+ipserviceprotocol=udp,......
 *              cn: aa
 *              cn: bb
 *
 * Note:
 *  Although the name switch/ldap's  rdn is in "cn=xx" or "cn=xx+..."
 * format, this function makes no such assumption. If the DN
 * is saved as "dn: yy=...+sn=my_canocical_name, ..", then it can still work.
 * The comments use "cn" as an example only.
 *
 */
typedef int (*cmpfunc)(const char *, const char *);

char *
__s_api_get_canonical_name(ns_ldap_entry_t *entry, ns_ldap_attr_t *attrptr,
                        int case_ignore) {
        uint_t                  i;
        char                    *token, *lasts, *value = NULL;
        char                    **rdn = NULL, **attrs = NULL, **values = NULL;
        char                    *rdn_attr_value = NULL;
        cmpfunc                 cmp;

        if (entry == NULL || attrptr == NULL)
                return (NULL);

        /* "values" is read-only */
        if ((values = __ns_ldap_getAttr(entry, "dn")) == NULL ||
            values[0] == NULL)
                return (NULL);

        if ((rdn = ldap_explode_dn(values[0], 0)) == NULL ||
            rdn[0] == NULL)
                return (NULL);

        if ((attrs = ldap_explode_rdn(rdn[0], 0)) == NULL) {
                ldap_value_free(rdn);
                return (NULL);
        }
        /* Assume the rdn is normalized */
        for (i = 0; attrs[i] != NULL; i++) {
                /* parse attribute name and value, get attribute name first */
                if ((token = strtok_r(attrs[i], "=", &lasts)) == NULL) {
                        goto cleanup;
                }
                if (strcasecmp(token, attrptr->attrname) == 0) {
                        /* get value */
                        rdn_attr_value = lasts;
                        break;
                }
        }
        if (rdn_attr_value) {
                if (case_ignore)
                        cmp = strcasecmp;
                else
                        cmp = strcmp;
                /*
                 * After parsing RDN and find the matching attribute in RDN,
                 * match rdn value with values in "cn".
                 */
                for (i = 0; i < attrptr->value_count; i++) {
                        if (attrptr->attrvalue[i] &&
                            (*cmp)(rdn_attr_value,
                            attrptr->attrvalue[i]) == 0) {
                                /* RDN "cn" value matches the "cn" value */
                                value = attrptr->attrvalue[i];
                                break;
                        }
                }
        }
cleanup:
        ldap_value_free(rdn);
        ldap_value_free(attrs);

        return (value);
}

/*
 * This function requests a server to be removed from
 * the cache manager maintained server list. This is
 * done via the door functionality.
 * Returns 0 if OK, else a negative value.
 */

int
__s_api_removeServer(const char *server)
{
        union {
                ldap_data_t     s_d;
                char            s_b[DOORBUFFERSIZE];
        } space;

        ns_server_info_t                r, *ret = &r;
        const char              *ireq;
        ldap_data_t             *sptr;
        int                     ndata;
        int                     adata;
        int                     len;
        int                     rc;
        ns_ldap_error_t         *error = NULL;

        if (server == NULL)
                return (-1);

        ireq = NS_CACHE_NORESP;

        if (__s_api_isStandalone()) {
                /*
                 * Remove 'server' from the standalone server list.
                 * __s_api_findRootDSE() is the standalone version
                 * of getldap_get_serverInfo() used in ldap_cachemgr.
                 * Request NS_CACHE_NORESP indicates 'server' should
                 * be removed.
                 */
                if (__s_api_findRootDSE(ireq,
                    server,
                    NS_CACHE_ADDR_IP,
                    NULL,
                    &error) != NS_LDAP_SUCCESS) {
                        syslog(LOG_WARNING,
                            "libsldap (\"standalone\" mode): "
                            " Unable to remove %s - %s",
                            server,
                            error != NULL && error->message != NULL ?
                            error->message : " no error info");
                        if (error != NULL) {
                                (void) __ns_ldap_freeError(&error);
                        }

                        return (NS_CACHE_NOSERVER);
                }

                return (0);
        }

        (void) memset(ret, 0, sizeof (ns_server_info_t));
        (void) memset(space.s_b, 0, DOORBUFFERSIZE);

        adata = (sizeof (ldap_call_t) + strlen(ireq) +
            strlen(NS_CACHE_ADDR_IP) + 1);
        adata += strlen(DOORLINESEP) + 1;
        adata += strlen(server) + 1;

        ndata = sizeof (space);
        space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER;
        len = sizeof (space) - sizeof (space.s_d.ldap_call.ldap_callnumber);
        if (strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len) >= len)
                return (-1);
        if (strlcat(space.s_d.ldap_call.ldap_u.domainname,
            NS_CACHE_ADDR_IP, len) >= len)
                return (-1);
        if (strlcat(space.s_d.ldap_call.ldap_u.domainname, DOORLINESEP, len) >=
            len)
                return (-1);
        if (strlcat(space.s_d.ldap_call.ldap_u.domainname, server, len) >= len)
                return (-1);
        sptr = &space.s_d;

        /* try to remove the server via the door interface */
        rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);

        /* clean up the door call */
        if (sptr != &space.s_d) {
                (void) munmap((char *)sptr, ndata);
        }

        return (rc);
}

void
__s_api_free_server_info(ns_server_info_t *sinfo) {
        if (sinfo->server) {
                free(sinfo->server);
                sinfo->server = NULL;
        }
        if (sinfo->serverFQDN) {
                free(sinfo->serverFQDN);
                sinfo->serverFQDN = NULL;
        }
        __s_api_free2dArray(sinfo->saslMechanisms);
        sinfo->saslMechanisms = NULL;
        __s_api_free2dArray(sinfo->controls);
        sinfo->controls = NULL;
}

/*
 * Create an ns_ldap_error structure, set status to 'rc',
 * and copy in the error message 'msg'.
 */
ns_ldap_error_t *
__s_api_make_error(int rc, char *msg) {
        ns_ldap_error_t *ep;

        ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
        if (ep == NULL)
                return (NULL);

        ep->status = rc;
        if (msg != NULL)
                ep->message =  strdup(msg); /* OK if ep->message is NULL */

        return (ep);
}

/*
 * Make a copy of the input ns_ldap_error.
 */
ns_ldap_error_t *
__s_api_copy_error(ns_ldap_error_t *errorp) {
        ns_ldap_error_t *ep;
        char            *msg;

        if (errorp == NULL)
                return (NULL);

        ep = (ns_ldap_error_t *)malloc(sizeof (*ep));
        if (ep != NULL) {
                *ep = *errorp;
                if (ep->message != NULL) {
                        msg = strdup(ep->message);
                        if (msg == NULL) {
                                free(ep);
                                ep = NULL;
                        } else
                                ep->message = msg;
                }
        }
        return (ep);
}