/*
* 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, 2011, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Public utilities and convenience calls (from the API spec):
* SLPFindScopes (queries for all known scopes)
* SLPEscape / Unescape
* SLPFree
* SLPSet/GetProperty
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <netdb.h>
#include <unistd.h>
#include <libintl.h>
#include <slp-internal.h>
struct scopes_tree {
void *scopes;
int len;
};
typedef SLPBoolean SLPScopeCallback(SLPHandle, const char *, SLPError, void *);
static SLPSrvURLCallback collate_scopes;
static void collect_scopes(void *, VISIT, int, void *);
static SLPBoolean unpackSAAdvert_scope(slp_handle_impl_t *, char *,
SLPScopeCallback, void *,
void **, int *);
static SLPError SAAdvert_for_scopes(SLPHandle, void **);
static SLPError slp_unescape(const char *, char **, SLPBoolean, const char);
/*
* Finds scopes according the the user administrative model.
*/
SLPError SLPFindScopes(SLPHandle hSLP, char **ppcScopes) {
SLPError err;
char *reply, *unesc_reply;
void *stree = NULL;
void *collator = NULL;
if (!hSLP || !ppcScopes) {
return (SLP_PARAMETER_BAD);
}
/* first try administratively configured scopes */
if ((err = slp_administrative_scopes(ppcScopes, SLP_FALSE))
!= SLP_OK) {
return (err);
}
if (*ppcScopes) {
/* got scopes */
return (SLP_OK);
}
/* DAs from active and passive discovery */
if ((err = slp_find_das("", &reply)) != SLP_OK &&
err != SLP_NETWORK_ERROR)
return (err);
/* Unpack the reply */
if (reply) {
int numResults = 0; /* placeholder; not actually used */
/* tag call as internal */
((slp_handle_impl_t *)hSLP)->internal_call = SLP_TRUE;
(void) slp_unpackSrvReply(
hSLP, reply, collate_scopes,
&stree, &collator, &numResults);
/* invoke last call */
(void) slp_unpackSrvReply(
hSLP, NULL, collate_scopes,
&stree, &collator, &numResults);
free(reply);
/* revert internal call tag */
((slp_handle_impl_t *)hSLP)->internal_call = SLP_FALSE;
}
/* Finally, if no results yet, try SA discovery */
if (!stree) {
(void) SAAdvert_for_scopes(hSLP, &stree);
}
if (!stree) {
/* found none, so just return "default" */
if (!(*ppcScopes = strdup("default"))) {
slp_err(LOG_CRIT, 0, "SLPFindScopes", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
return (SLP_OK);
}
/* we now have a btree, each leaf of which is a unique scope */
slp_twalk(stree, collect_scopes, 0, (void *) ppcScopes);
/* unescape scopes list */
if ((err = slp_unescape(*ppcScopes, &unesc_reply, SLP_FALSE, '%'))
== SLP_OK) {
free(*ppcScopes);
*ppcScopes = unesc_reply;
} else {
free(unesc_reply);
}
return (err);
}
/*
* Finds scopes according to the adminstrative scoping model. A
* comma-separated list of scopes is returned in *ppcScopes; the
* caller must free *ppcScopes.
* If the return_default parameter is true, and no scopes are found,
* *ppcScopes will be set to 'default', otherwise, *ppcScopes will
* be NULL. This helps simplify internal memory management.
*/
SLPError slp_administrative_scopes(char **ppcScopes,
SLPBoolean return_default) {
const char *useScopes;
*ppcScopes = NULL;
/* @@@ first try DHCP */
/* next try the useScopes property */
useScopes = SLPGetProperty(SLP_CONFIG_USESCOPES);
if (useScopes && *useScopes) {
if (!(*ppcScopes = strdup(useScopes))) {
slp_err(LOG_CRIT, 0, "SLPFindScopes", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
return (SLP_OK);
}
/* found none, so just return "default" */
if (return_default && !(*ppcScopes = strdup("default"))) {
slp_err(LOG_CRIT, 0, "SLPFindScopes", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
return (SLP_OK);
}
/*
* This function operates on the same btree as the collate_scopes().
* The difference is that this one is called for each
* SAAdvert recieved.
*/
/* ARGSUSED */
static SLPBoolean saadvert_callback(SLPHandle hp, char *scopes,
SLPError err, void **stree) {
char *s, *tstate;
if (err != SLP_OK) {
return (SLP_TRUE);
}
for (
s = strtok_r((char *)scopes, ",", &tstate);
s;
s = strtok_r(NULL, ",", &tstate)) {
char *ascope, **srch;
if (!(ascope = strdup(s))) { /* no memory! */
slp_err(LOG_CRIT, 0, "collate_scopes",
"out of memory");
return (SLP_TRUE);
}
srch = slp_tsearch(
(void *) ascope, stree,
(int (*)(const void *, const void *)) slp_strcasecmp);
if (*srch != ascope)
/* scope is already in there, so just free ascope */
free(ascope);
}
return (SLP_TRUE);
}
/*
* Generates an SAAdvert solicitation, and returns any scopes found
* from all recieved SAAdverts in stree. stree must be a btree
* structure.
*/
static SLPError SAAdvert_for_scopes(SLPHandle hSLP, void **stree) {
SLPError err;
SLPBoolean sync_state;
slp_handle_impl_t *hp = (slp_handle_impl_t *)hSLP;
char *predicate;
const char *type_hint;
/* get type hint, if set */
if ((type_hint = SLPGetProperty(SLP_CONFIG_TYPEHINT)) != NULL &&
*type_hint != 0) {
size_t hintlen = strlen(type_hint);
size_t predlen = strlen("(service-type=)");
/* check bounds */
if (hintlen > (SLP_MAX_STRINGLEN - predlen)) {
return (SLP_PARAMETER_BAD);
}
if (!(predicate = malloc(hintlen + predlen + 1))) {
slp_err(LOG_CRIT, 0, "SAAdvert_for_scopes",
"out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
(void) strcpy(predicate, "(service-type=");
(void) strcat(predicate, type_hint);
(void) strcat(predicate, ")");
} else {
predicate = "";
type_hint = NULL;
}
/* No callback for SLPFindScopes, so force synchronous mode only */
sync_state = hp->async;
hp->async = SLP_FALSE;
if ((err = slp_start_call(hp)) != SLP_OK)
return (err);
err = slp_packSrvRqst("service:service-agent", predicate, hp);
if (err == SLP_OK)
err = slp_ua_common(hSLP, "",
(SLPGenericAppCB *)saadvert_callback,
stree,
(SLPMsgReplyCB *)unpackSAAdvert_scope);
if (type_hint) {
free(predicate);
}
if (err != SLP_OK)
slp_end_call(hp);
/* restore sync state */
hp->async = sync_state;
return (err);
}
/*
* Unpack an SAAdvert and pass each set of scopes into cb.
*/
/* ARGSUSED */
static SLPBoolean unpackSAAdvert_scope(slp_handle_impl_t *hSLP, char *reply,
SLPScopeCallback cb, void *cookie,
void **collator, int *numResults) {
char *surl, *scopes, *attrs;
SLPBoolean cont;
if (!reply) {
cb(hSLP, NULL, SLP_LAST_CALL, cookie);
return (SLP_FALSE);
}
/* tag call as internal; gets all scopes, regardless of maxResults */
hSLP->internal_call = SLP_TRUE;
if (slp_unpackSAAdvert(reply, &surl, &scopes, &attrs) != SLP_OK) {
return (SLP_TRUE);
}
cont = cb(hSLP, scopes, SLP_OK, cookie);
/* revert internal_call tag */
hSLP->internal_call = SLP_FALSE;
free(surl);
free(scopes);
free(attrs);
return (cont);
}
/*
* Creates a service request for finding DAs or SAs (based on 'filter'),
* and sends it to slpd, returning the reply in 'reply'.
*/
SLPError slp_find_das(const char *filter, char **reply) {
SLPError err;
char *msg, hostname[MAXHOSTNAMELEN];
/* Try the cache first */
if (*reply = slp_find_das_cached(filter)) {
return (SLP_OK);
}
/*
* create a find scopes message:
* this is a SrvRqst for the type directory-agent.sun.
*/
/* use the local host's name for the scope */
(void) gethostname(hostname, MAXHOSTNAMELEN);
err = slp_packSrvRqst_single(
SLP_SUN_DA_TYPE, hostname, filter, &msg, "en");
if (err == SLP_OK) {
err = slp_send2slpd(msg, reply);
free(msg);
}
/* Add the reply to the cache */
if (err == SLP_OK) {
slp_put_das_cached(filter, *reply, slp_get_length(*reply));
}
return (err);
}
/*
* This is called for each URL entry in the DA service reply (sun private).
* Contained within the cookie is a btree, to which it adds new
* scopes from the URL entry. The scopes are retrieved from the btree
* by traversing the tree in SLPFindScopes().
* SLPHandle h is NULL, so don't touch it!
*/
/*ARGSUSED*/
static SLPBoolean collate_scopes(SLPHandle h, const char *u,
unsigned short lifetime,
SLPError errCode, void *cookie) {
SLPSrvURL *surl;
char *s, *tstate, *p, *url;
void **collator = cookie;
if (errCode != SLP_OK)
return (SLP_TRUE);
/* dup url so as not to corrupt da cache */
if (!(url = strdup(u))) {
slp_err(LOG_CRIT, 0, "collate_scopes", "out of memory");
return (SLP_FALSE);
}
/* parse url into a SLPSrvURL struct */
if (SLPParseSrvURL(url, &surl) != SLP_OK)
return (SLP_TRUE); /* bad URL; skip it */
/* collate the scopes using the btree stree->scopes: */
/* skip the 'scopes=' part */
if (!(p = strchr(surl->s_pcSrvPart, '='))) {
free(surl);
return (SLP_TRUE); /* bad URL; skip it */
}
p++;
for (
s = strtok_r(p, ",", &tstate);
s;
s = strtok_r(NULL, ",", &tstate)) {
char *ascope, **srch;
if (!(ascope = strdup(s))) { /* no memory! */
slp_err(LOG_CRIT, 0, "collate_scopes",
"out of memory");
free(surl);
return (SLP_TRUE);
}
srch = slp_tsearch(
(void *) ascope, collator,
(int (*)(const void *, const void *)) slp_strcasecmp);
if (*srch != ascope)
/* scope is already in there, so just free ascope */
free(ascope);
}
free(url);
free(surl);
return (SLP_TRUE);
}
/*
* Each time we visit a node for the last time, copy that scope into
* the scope collection and free the scope string and the node.
*/
/*ARGSUSED*/
static void collect_scopes(void *node, VISIT order, int level, void *cookie) {
char **scopes = (char **)cookie;
if (order == endorder || order == leaf) {
char *s = *(char **)node;
slp_add2list(s, scopes, SLP_FALSE);
free(s);
free(node);
}
}
void SLPFree(void *pvMem) {
if (pvMem)
free(pvMem);
}
/*
* Escape / Unescape
*/
#define isBadTagChar(c) ((c) == '*' || (c) == '_' || \
(c) == '\n' || (c) == '\t' || (c) == '\r')
#define isReserved(c) ((c) <= 31 || (c) == '(' || (c) == ')' || \
(c) == ',' || (c) == '\\' || (c) == '!' || \
(c) == '<' || (c) == '=' || (c) == '>' || \
(c) == '~')
SLPError SLPEscape(const char *pcInbuf, char **ppcOutBuf, SLPBoolean isTag) {
char *buf, *pin, *pout;
if (!pcInbuf || !ppcOutBuf)
return (SLP_PARAMETER_BAD);
if (!(buf = malloc(strlen(pcInbuf) * 3 + 1))) {
slp_err(LOG_CRIT, 0, "SLPEscape", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
*ppcOutBuf = buf;
for (pin = (char *)pcInbuf, pout = buf; *pin; ) {
int len;
/* If char is start of multibyte char, just copy it in */
if ((len = mblen(pin, MB_CUR_MAX)) > 1) {
int i;
for (i = 0; i < len && *pin; i++)
*pout++ = *pin++;
continue;
}
/* check for bad tag */
if (isTag && isBadTagChar(*pin))
return (SLP_PARSE_ERROR);
if (isReserved(*pin)) {
if (isTag)
return (SLP_PARSE_ERROR);
(void) sprintf(pout, "\\%.2x", *pin);
pout += 3;
pin++;
} else {
*pout++ = *pin++;
}
}
*pout = 0;
return (SLP_OK);
}
SLPError SLPUnescape(const char *pcInbuf, char **ppcOutBuf, SLPBoolean isTag) {
if (!pcInbuf || !ppcOutBuf)
return (SLP_PARAMETER_BAD);
return (slp_unescape(pcInbuf, ppcOutBuf, isTag, '\\'));
}
/*
* The actual unescaping routine; allows for different escape chars.
*/
static SLPError slp_unescape(const char *pcInbuf, char **ppcOutBuf,
SLPBoolean isTag, const char esc_char) {
char *buf, *pin, *pout, conv[3];
if (!(buf = malloc(strlen(pcInbuf) * 3 + 1))) {
slp_err(LOG_CRIT, 0, "SLPEscape", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
*ppcOutBuf = buf;
conv[2] = 0;
for (pin = (char *)pcInbuf, pout = buf; *pin; ) {
int len;
/* If char is start of multibyte char, just copy it in */
if ((len = mblen(pin, MB_CUR_MAX)) > 1) {
int i;
for (i = 0; i < len && *pin; i++)
*pout++ = *pin++;
continue;
}
if (*pin == esc_char) {
if (!pin[1] || !pin[2])
return (SLP_PARSE_ERROR);
pin++;
conv[0] = *pin++;
conv[1] = *pin++;
*pout++ = (char)strtol(conv, NULL, 16);
if (isTag && isBadTagChar(*pout))
return (SLP_PARSE_ERROR);
} else {
*pout++ = *pin++;
}
}
*pout = 0;
return (SLP_OK);
}
/*
* Properties
*
* All properties are stored in a global tree (prop_table). This
* tree is created and accessed by slp_tsearch and slp_tfind.
*/
struct prop_entry {
const char *key, *val;
};
typedef struct prop_entry slp_prop_entry_t;
/* Global properties table */
static void *slp_props = NULL;
static mutex_t prop_table_lock = DEFAULTMUTEX;
static void setDefaults();
static int compare_props(const void *a, const void *b) {
return (strcmp(
((slp_prop_entry_t *)a)->key,
((slp_prop_entry_t *)b)->key));
}
void SLPSetProperty(const char *pcName, const char *pcValue) {
slp_prop_entry_t *pe, **pe2;
if (!slp_props) setDefaults();
if (!pcName || !pcValue) {
return;
}
if (!(pe = malloc(sizeof (*pe)))) {
slp_err(LOG_CRIT, 0, "SLPSetProperty", "out of memory");
return;
}
/* place the strings under library ownership */
if (!(pe->key = strdup(pcName))) {
free(pe);
slp_err(LOG_CRIT, 0, "SLPSetProperty", "out of memory");
return;
}
if (!(pe->val = strdup(pcValue))) {
free((void *) pe->key);
free(pe);
slp_err(LOG_CRIT, 0, "SLPSetProperty", "out of memory");
return;
}
/* is pcName already set? */
(void) mutex_lock(&prop_table_lock);
pe2 = slp_tsearch((void *) pe, &slp_props, compare_props);
if (pe != *pe2) {
/* this prop is already set; overwrite the old value */
free((void *) (*pe2)->val);
(*pe2)->val = pe->val;
free((void *) pe->key);
free(pe);
}
(void) mutex_unlock(&prop_table_lock);
}
const char *SLPGetProperty(const char *pcName) {
slp_prop_entry_t pe[1], **ans;
if (!slp_props) setDefaults();
if (!pcName) {
return (NULL);
}
pe->key = pcName;
(void) mutex_lock(&prop_table_lock);
ans = slp_tfind(pe, &slp_props, compare_props);
(void) mutex_unlock(&prop_table_lock);
if (ans)
return ((*ans)->val);
return (NULL);
}
static void setDefaults() {
slp_prop_entry_t *pe;
static mutex_t lock = DEFAULTMUTEX;
(void) mutex_lock(&lock);
if (slp_props) {
(void) mutex_unlock(&lock);
return;
}
pe = malloc(sizeof (*pe));
pe->key = strdup(SLP_CONFIG_ISBROADCASTONLY);
pe->val = strdup("false");
(void) slp_tsearch((void *) pe, &slp_props, compare_props);
pe = malloc(sizeof (*pe));
pe->key = strdup(SLP_CONFIG_MULTICASTTTL);
pe->val = strdup("255");
(void) slp_tsearch((void *) pe, &slp_props, compare_props);
pe = malloc(sizeof (*pe));
pe->key = strdup(SLP_CONFIG_MULTICASTMAXWAIT);
pe->val = strdup("15000");
(void) slp_tsearch((void *) pe, &slp_props, compare_props);
pe = malloc(sizeof (*pe));
pe->key = strdup(SLP_CONFIG_DATAGRAMTIMEOUTS);
pe->val = strdup("2000,2000,2000");
(void) slp_tsearch((void *) pe, &slp_props, compare_props);
pe = malloc(sizeof (*pe));
pe->key = strdup(SLP_CONFIG_MULTICASTTIMEOUTS);
pe->val = strdup("1000,3000,3000,3000,3000");
(void) slp_tsearch((void *) pe, &slp_props, compare_props);
pe = malloc(sizeof (*pe));
pe->key = SLP_CONFIG_MTU; pe->val = "1400";
(void) slp_tsearch((void *) pe, &slp_props, compare_props);
pe = malloc(sizeof (*pe));
pe->key = strdup(SLP_CONFIG_MAXRESULTS);
pe->val = strdup("-1");
(void) slp_tsearch((void *) pe, &slp_props, compare_props);
pe = malloc(sizeof (*pe));
pe->key = strdup(SLP_CONFIG_SECURITY_ON);
pe->val = strdup("false");
(void) slp_tsearch((void *) pe, &slp_props, compare_props);
pe = malloc(sizeof (*pe));
pe->key = strdup(SLP_CONFIG_BYPASS_AUTH);
pe->val = strdup("false");
(void) slp_tsearch((void *) pe, &slp_props, compare_props);
slp_readConfig();
(void) mutex_unlock(&lock);
}
static const char *error_strings[] = {
"OK", /* 0 */
"Language not supported", /* -1 */
"Parse error", /* -2 */
"Invalid registration", /* -3 */
"Scope not supported", /* -4 */
"Invalid error number", /* -5 */
"Authentication absent", /* -6 */
"Authentication failed", /* -7 */
"Invalid error number", /* -8 */
"Invalid error number", /* -9 */
"Invalid error number", /* -10 */
"Invalid error number", /* -11 */
"Invalid error number", /* -12 */
"Invalid update", /* -13 */
"Invalid error number", /* -14 */
"Invalid error number", /* -15 */
"Invalid error number", /* -16 */
"Not implemented", /* -17 */
"Buffer overflow", /* -18 */
"Network timed out", /* -19 */
"Network init failed", /* -20 */
"Memory alloc failed", /* -21 */
"Parameter bad", /* -22 */
"Network error", /* -23 */
"Internal system error", /* -24 */
"Handle in use", /* -25 */
"Type error" /* -26 */
};
#define SLP_MAX_ERR_CNT 26
const char *slp_strerror(SLPError err) {
int abserr;
const char *str;
if (err == SLP_LAST_CALL) {
str = "Last call";
} else if (err == SLP_SECURITY_UNAVAILABLE) {
str = "Security Implementation Unavailable";
} else {
abserr = abs(err);
if (abserr > SLP_MAX_ERR_CNT) {
str = "Invalid error number";
} else {
str = error_strings[abserr];
}
}
return (dgettext(TEXT_DOMAIN, str));
}