/*
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <slp-internal.h>
struct surl_node {
char *surl;
unsigned short lifetime;
};
struct caller_bundle {
SLPSrvURLCallback *cb;
void *cookie;
SLPHandle handle;
};
static int compare_surls(struct surl_node *, struct surl_node *);
static char *collate_surls(char *, unsigned short, void **);
static void traverse_surls(SLPHandle, SLPSrvURLCallback, void *, void *);
static void process_surl_node(void *, VISIT, int, void *);
static SLPBoolean unpackDAAdvert_srv(slp_handle_impl_t *, char *,
SLPSrvURLCallback, void *,
void **, int *);
static SLPBoolean unpackSAAdvert_srv(slp_handle_impl_t *, char *,
SLPSrvURLCallback, void *,
void **, int *);
SLPError SLPFindSrvs(SLPHandle hSLP, const char *pcServiceType,
const char *pcScope, const char *pcSearchFilter,
SLPSrvURLCallback callback, void *pvUser) {
SLPError err;
slp_handle_impl_t *hp = (slp_handle_impl_t *)hSLP;
int wantSAAdvert =
strcasecmp(pcServiceType, "service:service-agent") == 0;
int wantDAAdvert =
strcasecmp(pcServiceType, "service:directory-agent") == 0;
int isSpecial = wantSAAdvert || wantDAAdvert;
SLPMsgReplyCB *unpack_cb;
if (!hSLP || !pcServiceType || !pcScope || (!*pcScope && !isSpecial) ||
!pcSearchFilter || !callback) {
return (SLP_PARAMETER_BAD);
}
if ((strlen(pcServiceType) > SLP_MAX_STRINGLEN) ||
(strlen(pcScope) > SLP_MAX_STRINGLEN) ||
(strlen(pcSearchFilter) > SLP_MAX_STRINGLEN)) {
return (SLP_PARAMETER_BAD);
}
if ((err = slp_start_call(hSLP)) != SLP_OK)
return (err);
/* Special unpacker for DA and SA solicitations */
if (wantDAAdvert) {
unpack_cb = (SLPMsgReplyCB *)unpackDAAdvert_srv;
hp->force_multicast = SLP_TRUE;
} else if (wantSAAdvert) {
unpack_cb = (SLPMsgReplyCB *)unpackSAAdvert_srv;
hp->force_multicast = SLP_TRUE;
} else {
/* normal service request */
unpack_cb = (SLPMsgReplyCB *)slp_unpackSrvReply;
}
err = slp_packSrvRqst(pcServiceType, pcSearchFilter, hp);
if (err == SLP_OK)
err = slp_ua_common(hSLP, pcScope,
(SLPGenericAppCB *) callback, pvUser,
unpack_cb);
if (err != SLP_OK)
slp_end_call(hSLP);
return (err);
}
SLPBoolean slp_unpackSrvReply(slp_handle_impl_t *hp, char *reply,
SLPSrvURLCallback cb, void *cookie,
void **collator, int *numResults) {
SLPError errCode;
unsigned short urlCount, protoErrCode;
size_t len, off;
int i;
int maxResults = slp_get_maxResults();
SLPBoolean cont = SLP_TRUE;
if (!reply) {
/* no more results */
/* traverse_surls:invoke cb for sync case,and free resources */
if (!hp->async) {
traverse_surls(hp, cb, cookie, *collator);
}
cb(hp, NULL, 0, SLP_LAST_CALL, cookie);
return (SLP_FALSE);
}
len = slp_get_length(reply);
off = SLP_HDRLEN + slp_get_langlen(reply);
/* err 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, 0, errCode, cookie));
}
/* url entry count */
if (slp_get_sht(reply, len, &off, &urlCount) != SLP_OK)
return (SLP_TRUE);
/* for each srvRply, unpack and pass to CB */
for (i = 0; i < urlCount && !hp->cancel; i++) {
char *pcSrvURL;
unsigned short sLifetime;
int nURLAuthBlocks;
size_t tbv_len;
char *url_tbv;
/* parse URL entry into params */
off++; /* skip reserved byte */
/* lifetime */
if (slp_get_sht(reply, len, &off, &sLifetime) != SLP_OK)
return (SLP_TRUE);
/* URL itself; keep track of it in case we need to verify */
url_tbv = reply + off;
tbv_len = off;
if (slp_get_string(reply, len, &off, &pcSrvURL) != SLP_OK)
return (SLP_TRUE);
tbv_len = off - tbv_len;
/* number of url auths */
if (slp_get_byte(reply, len, &off, &nURLAuthBlocks) != SLP_OK)
goto cleanup;
/* get and verify auth blocks */
if ((!hp->internal_call && slp_get_security_on()) ||
nURLAuthBlocks > 0) {
struct iovec iov[1];
size_t abLen = 0;
iov[0].iov_base = url_tbv;
iov[0].iov_len = tbv_len;
if (slp_verify(iov, 1,
reply + off,
len - off,
nURLAuthBlocks,
&abLen) != SLP_OK) {
goto cleanup;
}
off += abLen;
}
/* collate the srv urls for sync behavior */
if (!hp->async) {
pcSrvURL = collate_surls(pcSrvURL, sLifetime, collator);
if (!pcSrvURL)
continue;
}
(*numResults)++;
/* invoke cb */
if (hp->async)
cont = cb(
(SLPHandle) hp,
pcSrvURL,
sLifetime,
errCode,
cookie);
/* cleanup */
cleanup:
free(pcSrvURL);
/* check maxResults */
if (!hp->internal_call && *numResults == maxResults) {
cont = SLP_FALSE;
}
if (!cont) break;
}
return (cont);
}
/*
* unpackDAAdvert_srv follows the same same logic flow as slp_unpackSrvReply
* with two differences: the message in reply is a DAAdvert, and
* this function is not used internally, so hp is never NULL. Although
* all info from a DAAdvert is returned by slp_unpackDAAdvert, here
* the recipient (the user-supplied SLPSrvURLCallback) is interested
* only in the DA service URL.
*/
static SLPBoolean unpackDAAdvert_srv(slp_handle_impl_t *hp, char *reply,
SLPSrvURLCallback cb, void *cookie,
void **collator, int *numResults) {
char *surl, *scopes, *attrs, *spis;
SLPBoolean cont = SLP_TRUE;
SLPError errCode;
int maxResults = slp_get_maxResults();
if (!reply) {
/* no more results */
/* traverse_surls:invoke cb for sync case,and free resources */
if (!hp->async) {
traverse_surls(hp, cb, cookie, *collator);
}
cb(hp, NULL, 0, SLP_LAST_CALL, cookie);
return (SLP_FALSE);
}
if (slp_unpackDAAdvert(reply, &surl, &scopes, &attrs, &spis, &errCode)
!= SLP_OK) {
return (SLP_TRUE);
}
if (errCode != SLP_OK) {
return (cb(hp, NULL, 0, errCode, cookie));
}
/* collate the urls */
surl = collate_surls(surl, 0, collator);
if (!surl) {
return (SLP_TRUE);
}
(*numResults)++;
if (hp->async) {
cont = cb((SLPHandle)hp, surl, 0, errCode, cookie);
}
/* cleanup */
free(surl);
free(scopes);
free(attrs);
free(spis);
/* check maxResults */
if (!hp->internal_call && *numResults == maxResults) {
return (SLP_FALSE);
}
return (cont);
}
/*
* unpackSAAdvert_srv follows the same same logic flow as slp_unpackSrvReply
* with two differences: the message in reply is a SAAdvert, and
* this function is not used internally, so hp is never NULL. Although
* all info from an SAAdvert is returned by slp_unpackSAAdvert, here
* the recipient (the user-supplied SLPSrvURLCallback) is interested
* only in the SA service URL.
*/
static SLPBoolean unpackSAAdvert_srv(slp_handle_impl_t *hp, char *reply,
SLPSrvURLCallback cb, void *cookie,
void **collator, int *numResults) {
char *surl, *scopes, *attrs;
SLPBoolean cont = SLP_TRUE;
int maxResults = slp_get_maxResults();
if (!reply) {
/* no more results */
/* traverse_surls:invoke cb for sync case,and free resources */
if (!hp->async) {
/* sync case */
traverse_surls(hp, cb, cookie, *collator);
}
cb(hp, NULL, 0, SLP_LAST_CALL, cookie);
return (SLP_FALSE);
}
if (slp_unpackSAAdvert(reply, &surl, &scopes, &attrs) != SLP_OK) {
return (SLP_TRUE);
}
/* collate the urls */
surl = collate_surls(surl, 0, collator);
if (!surl) {
return (SLP_TRUE);
}
(*numResults)++;
if (hp->async) {
cont = cb((SLPHandle)hp, surl, 0, SLP_OK, cookie);
}
/* cleanup */
free(surl);
free(scopes);
free(attrs);
/* check maxResults */
if (!hp->internal_call && *numResults == maxResults) {
return (SLP_FALSE);
}
return (cont);
}
SLPError slp_packSrvRqst(const char *type,
const char *filter,
slp_handle_impl_t *hp) {
SLPError err;
size_t len, msgLen, tmplen;
slp_msg_t *msg = &(hp->msg);
char *spi = NULL;
if (slp_get_security_on()) {
spi = (char *)SLPGetProperty(SLP_CONFIG_SPI);
}
if (!spi || !*spi) {
spi = "";
}
/*
* Allocate iovec for the messge. A SrvRqst is layed out thus:
* 0: header
* 1: prlist length
* 2: prlist (filled in later by networking code)
* 3: service type string
* 4: scopes length
* 5: scopes (filled in later by networking code)
* 6: predicate string and SPI string
*/
if (!(msg->iov = calloc(7, sizeof (*(msg->iov))))) {
slp_err(LOG_CRIT, 0, "slp_packSrvRqst", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
msg->iovlen = 7;
/* calculate msg length */
msgLen = 2 + /* prlist length */
2 + strlen(type) + /* service type */
2 + /* scope list length */
2 + strlen(filter) + /* predicate string */
2 + strlen(spi); /* SPI string */
if (!(msg->msg = calloc(1, msgLen))) {
free(msg->iov);
slp_err(LOG_CRIT, 0, "slp_packSrvRqst", "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;
/* Add type string */
msg->iov[3].iov_base = msg->msg + len;
tmplen = len;
err = slp_add_string(msg->msg, msgLen, type, &len);
msg->iov[3].iov_len = len - tmplen;
if (err != SLP_OK)
goto error;
/* Add search filter */
msg->iov[6].iov_base = msg->msg + len;
tmplen = len;
err = slp_add_string(msg->msg, msgLen, filter, &len);
if (err != SLP_OK)
goto error;
err = slp_add_string(msg->msg, msgLen, spi, &len);
msg->iov[6].iov_len = len - tmplen;
hp->fid = SRVRQST;
if (err == SLP_OK) {
return (err);
}
/* else error */
error:
free(msg->iov);
free(msg->msg);
return (err);
}
/*
* Caller must free msg
*/
SLPError slp_packSrvRqst_single(const char *type,
const char *scopes,
const char *filter,
char **msg,
const char *lang) {
SLPError err;
size_t len, msgLen;
msgLen =
SLP_HDRLEN + strlen(lang) + 2 +
2 + strlen(type) +
2 + strlen(scopes) +
2 + strlen(filter) +
2; /* No SPI string for internal calls */
if (!(*msg = calloc(msgLen, 1))) {
slp_err(LOG_CRIT, 0, "slp_packSrvRqst_single",
"out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
len = 0;
err = slp_add_header(lang, *msg, msgLen, SRVRQST, msgLen, &len);
len += 2; /* empty PR list */
if (err == SLP_OK)
err = slp_add_string(*msg, msgLen, type, &len);
if (err == SLP_OK)
err = slp_add_string(*msg, msgLen, scopes, &len);
if (err == SLP_OK)
err = slp_add_string(*msg, msgLen, filter, &len);
if (err == SLP_OK)
/* empty SPI string */
err = slp_add_string(*msg, msgLen, "", &len);
return (err);
}
static int compare_surls(struct surl_node *s1, struct surl_node *s2) {
if (s1->lifetime != s2->lifetime)
return (s1->lifetime - s2->lifetime);
return (slp_strcasecmp(s1->surl, s2->surl));
}
/*
* Using the collator, determine if this URL has already been processed.
* If so, free surl and return NULL, else return the URL.
*/
static char *collate_surls(char *surl, unsigned short life, void **collator) {
struct surl_node *n, **res;
if (!(n = malloc(sizeof (*n)))) {
slp_err(LOG_CRIT, 0, "collate_surls", "out of memory");
return (NULL);
}
if (!(n->surl = strdup(surl))) {
free(n);
slp_err(LOG_CRIT, 0, "collate_surls", "out of memory");
return (NULL);
}
n->lifetime = life;
res = slp_tsearch((void *) n, collator,
(int (*)(const void *, const void *)) compare_surls);
if (*res == n) {
/* first time we've encountered this url */
return (surl);
}
/* else already in tree */
free(n->surl);
free(n);
free(surl);
return (NULL);
}
static void traverse_surls(SLPHandle h, SLPSrvURLCallback cb,
void *cookie, void *collator) {
struct caller_bundle caller[1];
if (!collator)
return;
caller->cb = cb;
caller->cookie = cookie;
caller->handle = h;
slp_twalk(collator, process_surl_node, 0, caller);
}
/*ARGSUSED*/
static void process_surl_node(void *node, VISIT order, int level, void *c) {
struct surl_node *n;
SLPSrvURLCallback *cb;
slp_handle_impl_t *h;
struct caller_bundle *caller = (struct caller_bundle *)c;
if (order == endorder || order == leaf) {
SLPBoolean cont = SLP_TRUE;
cb = caller->cb;
h = (slp_handle_impl_t *)caller->handle;
n = *(struct surl_node **)node;
/* invoke cb */
if (cont && (!h || !h->async))
cont = cb(
h, n->surl,
n->lifetime,
SLP_OK,
caller->cookie);
free(n->surl);
free(n);
free(node);
}
}