root/usr/src/lib/libslp/clib/SLPFindSrvTypes.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#include <syslog.h>
#include <slp-internal.h>

static SLPBoolean UnpackSrvTypesReply(slp_handle_impl_t *, char *,
                                        SLPSrvTypeCallback, void *,
                                        void **, int *);
static SLPError slp_packSrvTypeRqst(slp_handle_impl_t *, const char *);
static char *collate_types(char *, void **, int *, int);
static char *build_types_list(void *);
static void collect_types(void *, VISIT, int, void *);

SLPError SLPFindSrvTypes(SLPHandle hSLP, const char *pcNamingAuthority,
                                const char *pcScopeList,
                                SLPSrvTypeCallback callback, void *pvUser) {
        SLPError err;

        if (!hSLP || !pcNamingAuthority || !pcScopeList ||
            !*pcScopeList || !callback) {
                return (SLP_PARAMETER_BAD);
        }

        if ((strlen(pcNamingAuthority) > SLP_MAX_STRINGLEN) ||
            (strlen(pcScopeList) > SLP_MAX_STRINGLEN)) {
            return (SLP_PARAMETER_BAD);
        }

        if ((err = slp_start_call(hSLP)) != SLP_OK)
                return (err);

        /* format params into msgBuf */
        err = slp_packSrvTypeRqst(hSLP, pcNamingAuthority);

        if (err == SLP_OK)
                err = slp_ua_common(hSLP, pcScopeList,
                    (SLPGenericAppCB *)(uintptr_t)callback, pvUser,
                    (SLPMsgReplyCB *) UnpackSrvTypesReply);

        if (err != SLP_OK)
                slp_end_call(hSLP);

        return (err);
}

static SLPBoolean UnpackSrvTypesReply(slp_handle_impl_t *hp, char *reply,
                                        SLPSrvTypeCallback cb, void *cookie,
                                        void **collator, int *numResults) {
        char *pcSrvTypes;
        SLPError errCode;
        unsigned short protoErrCode;
        size_t off, len;
        int maxResults = slp_get_maxResults();
        SLPBoolean cont = SLP_TRUE;

        if (!reply) {
                /* no more results */
                if (!hp->async) {
                    pcSrvTypes = build_types_list(*collator);
                }

                if (!hp->async && pcSrvTypes) {
                    /* synchronous case */
                    cb(hp, pcSrvTypes, SLP_OK, cookie);
                    free(pcSrvTypes);
                }
                cb(hp, NULL, SLP_LAST_CALL, cookie);
                return (SLP_FALSE);
        }

        /* parse reply into params */
        len = slp_get_length(reply);
        off = SLP_HDRLEN + slp_get_langlen(reply);
        /* error code */
        if (slp_get_sht(reply, len, &off, &protoErrCode) != SLP_OK)
                return (SLP_TRUE);
        /* internal errors should have been filtered out by the net code */
        if ((errCode = slp_map_err(protoErrCode)) != SLP_OK) {
                return (cb(hp, NULL, errCode, cookie));
        }

        /* types string */
        if (slp_get_string(reply, len, &off, &pcSrvTypes) != SLP_OK)
                return (SLP_TRUE);

        /* collate the types for sync behavior */
        if (!hp->async) {
            pcSrvTypes = collate_types(pcSrvTypes, collator,
                                        numResults, maxResults);
            if (!pcSrvTypes)
                return (SLP_TRUE);
        } else {
            /* async; invoke cb */
            cont = cb((SLPHandle) hp, pcSrvTypes, errCode, cookie);
        }

        /* cleanup */
        free(pcSrvTypes);

        /* check maxResults */
        if (!hp->internal_call && *numResults == maxResults) {
                return (SLP_FALSE);
        }

        return (cont);
}

static SLPError slp_packSrvTypeRqst(slp_handle_impl_t *hp, const char *na) {
        SLPError err;
        size_t len, nalen, msgLen, tmplen;
        int all_nas;
        slp_msg_t *msg = &(hp->msg);

        /*
         * Allocate iovec for the message. A SrvTypeRqst is layed out thus:
         *  0: header
         *  1: prlist length
         *  2: prlist (filled in later by networking code)
         *  3: na
         *  4: scopes length
         *  5: scopes (filled in later by networking code)
         */
        if (!(msg->iov = calloc(6, sizeof (*(msg->iov))))) {
                slp_err(LOG_CRIT, 0, "slp_packSrvTypeRqst", "out of memory");
                return (SLP_MEMORY_ALLOC_FAILED);
        }
        msg->iovlen = 6;

        /* calculate msg length */
        all_nas = strcmp(na, "*") == 0 ? 1 : 0;
        if (all_nas) {
                nalen = 0;
        } else {
                nalen = strlen(na);
        }
        nalen += 2;

        msgLen = 2 +    /* prlist length */
            nalen +     /* NA string */
            2;          /* Scope string length */

        if (!(msg->msg = calloc(1, msgLen))) {
                free(msg->iov);
                slp_err(LOG_CRIT, 0, "slp_packSrvTypeRqst", "out of memory");
                return (SLP_MEMORY_ALLOC_FAILED);
        }

        /* set pointer to PR list and scope list length spaces */
        msg->prlistlen.iov_base = msg->msg;
        msg->prlistlen.iov_len = 2;
        msg->iov[1].iov_base = msg->msg;
        msg->iov[1].iov_len = 2;

        msg->scopeslen.iov_base = msg->msg + 2;
        msg->scopeslen.iov_len = 2;
        msg->iov[4].iov_base = msg->msg + 2;
        msg->iov[4].iov_len = 2;

        /* set up the scopes and prlist pointers into iov */
        msg->prlist = &(msg->iov[2]);
        msg->scopes = &(msg->iov[5]);

        len = 4;

        /* set up NA string in iovec */
        msg->iov[3].iov_base = msg->msg + len;
        tmplen = len;

        if (all_nas) {
                err = slp_add_sht(msg->msg, msgLen, 0xffff, &len);
        } else {
                err = slp_add_string(msg->msg, msgLen, na, &len);
        }
        msg->iov[3].iov_len = len - tmplen;

        hp->fid = SRVTYPERQST;
        if (err == SLP_OK) {
                return (SLP_OK);
        }

        /* else error */
        free(msg->iov);
        free(msg->msg);

        return (err);
}

/*
 * Using the collator, determines which types in the types list
 * have already been recieved, and composes a new list of the remaining
 * (unique) types. If there are no unique types, returns NULL;
 * types is destructively modified.
 */
static char *collate_types(char *types, void **collator,
                                int *numResults, int maxResults) {
        char *p, *s, **res, *utypes = NULL;

        /* walk through the types list */
        p = types;
        for (s = types; p && *numResults != maxResults; s = p) {
                p = slp_utf_strchr(s, ',');
                if (p)
                        *p++ = 0;
                if (!(s = strdup(s))) {
                    free(types);
                    if (utypes) free(utypes);
                    slp_err(LOG_CRIT, 0, "collate_types", "out of memory");
                    return (NULL);
                }
                /* search the tree for this type */
                res = slp_tsearch((void *) s, collator,
                        (int (*)(const void *, const void *)) slp_strcasecmp);
                if (*res == s) {
                        /* first time we've encountered this type */
                        slp_add2list(s, &utypes, SLP_FALSE);
                        (*numResults)++;
                } else {
                        /* else  already in tree */
                        free(s);
                }
        }
        free(types);
        return (utypes);
}

/*
 * This is used after all types have been collated into the tree.
 * It walks through the tree, composing a list from all the types in
 * the tree, and freeing each node of the tree as it goes.
 * Returns the list, or NULL if the tree is empty.
 */
/* the walk action function: */
/*ARGSUSED*/
static void collect_types(void *node, VISIT order, int level, void *cookie) {
        char **types = (char **)cookie;

        if (order == endorder || order == leaf) {
                char *t = *(char **)node;
                slp_add2list(t, types, SLP_FALSE);
                free(t);
                free(node);
        }
}

/* the walk driver: */
static char *build_types_list(void *collator) {
        char *types = NULL;

        if (!collator)
                return (NULL);
        slp_twalk(collator, collect_types, 0, (void *) &types);
        return (types);
}