#ifdef SLP
#include <stdio.h>
#include <slp.h>
#include <stdlib.h>
#include <string.h>
#include <door.h>
#include <unistd.h>
#include "ns_sldap.h"
#include "ns_internal.h"
#include "cachemgr.h"
#define ABSTYPE "service:naming-directory"
#define CONTEXT_ATTR "naming-context"
#define LDAP_DOMAIN_ATTR "x-sun-rpcdomain"
struct config_cookie {
SLPHandle h;
const char *type;
char *scopes;
const char *context_attr;
void *cache_cfg;
void *(*get_cfghandle)(const char *);
void (*aggregate)(void *, const char *, const char *);
void (*set_cfghandle)(void *);
};
extern admin_t current_admin;
static const char *getlocale() {
const char *locale = SLPGetProperty("net.slp.locale");
return (locale ? locale : "en");
}
static SLPBoolean next_attr(char **t_inout, char **v_inout,
char **s_inout, int *type) {
char *end = NULL;
char *tag = NULL;
char *val = NULL;
char *state = NULL;
if (!t_inout || !v_inout)
return (SLP_FALSE);
if (!s_inout || !*s_inout || !**s_inout)
return (SLP_FALSE);
state = *s_inout;
switch (*type) {
case 0:
switch (*state) {
case '(':
*type = 1;
break;
case ',':
state++;
*type = 0;
break;
default:
*type = 2;
}
*s_inout = state;
return (next_attr(t_inout, v_inout, s_inout, type));
break;
case 1:
switch (*state) {
case '(':
state++;
tag = state;
end = strchr(state, ')');
if (!end)
return (SLP_FALSE);
state = strchr(tag, '=');
if (state) {
if (state > end)
return (SLP_FALSE);
*state++ = 0;
} else {
return (SLP_FALSE);
}
default:
if (!end) {
tag = *t_inout;
end = strchr(state, ')');
if (!end)
return (SLP_FALSE);
}
val = state;
state = strchr(val, ',');
if (!state || state > end) {
state = end;
*type = 0;
}
*state++ = 0;
break;
}
break;
case 2:
tag = state;
state = strchr(tag, ',');
if (state) {
*state++ = 0;
}
val = NULL;
*type = 0;
break;
default:
return (SLP_FALSE);
}
*t_inout = tag;
*v_inout = val;
*s_inout = state;
return (SLP_TRUE);
}
static SLPBoolean aggregate_attrs(SLPHandle h, const char *attrs_in,
SLPError errin, void *cookie) {
char *tag, *val, *state;
char *unesc_tag, *unesc_val;
int type = 0;
char *attrs;
SLPError err;
struct config_cookie *cfg = (struct config_cookie *)cookie;
if (errin != SLP_OK) {
return (SLP_TRUE);
}
attrs = strdup(attrs_in);
state = attrs;
while (next_attr(&tag, &val, &state, &type)) {
unesc_tag = unesc_val = NULL;
if (tag) {
if ((err = SLPUnescape(tag, &unesc_tag, SLP_TRUE)) != SLP_OK) {
unesc_tag = NULL;
if (current_admin.debug_level >= DBG_ALL) {
(void) logit("aggregate_attrs: ",
"could not unescape attr tag %s:%s\n",
tag, slp_strerror(err));
}
}
}
if (val) {
if ((err = SLPUnescape(val, &unesc_val, SLP_FALSE))
!= SLP_OK) {
unesc_val = NULL;
if (current_admin.debug_level >= DBG_ALL) {
(void) logit("aggregate_attrs: ",
"could not unescape attr val %s:%s\n",
val, slp_strerror(err));
}
}
}
if (current_admin.debug_level >= DBG_ALL) {
(void) logit("discovery:\t\t%s=%s\n",
(unesc_tag ? unesc_tag : "NULL"),
(unesc_val ? unesc_val : "NULL"));
}
cfg->aggregate(cfg->cache_cfg, unesc_tag, unesc_val);
if (unesc_tag) free(unesc_tag);
if (unesc_val) free(unesc_val);
}
if (attrs) free(attrs);
return (SLP_TRUE);
}
static SLPBoolean foreach_server(SLPHandle hin, const char *u,
unsigned short life,
SLPError errin, void *cookie) {
SLPError err;
struct config_cookie *cfg = (struct config_cookie *)cookie;
SLPHandle h = cfg->h;
SLPSrvURL *surl = NULL;
char *url = NULL;
if (errin != SLP_OK) {
return (SLP_TRUE);
}
if (!(url = strdup(u))) {
(void) logit("foreach_server: no memory");
return (SLP_FALSE);
}
if ((err = SLPParseSrvURL(url, &surl)) != SLP_OK) {
free(url);
if (current_admin.debug_level >= DBG_NETLOOKUPS) {
(void) logit("foreach_server: ",
"dropping unparsable URL %s: %s\n",
url, slp_strerror(err));
return (SLP_TRUE);
}
}
if (current_admin.debug_level >= DBG_ALL) {
(void) logit("discovery:\tserver: %s\n", surl->s_pcHost);
}
err = SLPFindAttrs(h, u, cfg->scopes, "", aggregate_attrs, cookie);
if (err != SLP_OK) {
if (current_admin.debug_level >= DBG_NETLOOKUPS) {
(void) logit("foreach_server: FindAttrs failed: %s\n",
slp_strerror(err));
}
goto cleanup;
}
cfg->aggregate(cfg->cache_cfg, "_,_xservers_,_", surl->s_pcHost);
cleanup:
if (url) free(url);
if (surl) SLPFree(surl);
return (SLP_TRUE);
}
static void update_config(const char *context, struct config_cookie *cookie) {
SLPHandle h = NULL;
SLPHandle persrv_h = NULL;
SLPError err;
char *search = NULL;
char *unesc_domain = NULL;
if ((err = SLPUnescape(context, &unesc_domain, SLP_FALSE)) != SLP_OK) {
if (current_admin.debug_level >= DBG_ALL) {
(void) logit("update_config: ",
"dropping unparsable domain: %s: %s\n",
context, slp_strerror(err));
}
return;
}
cookie->cache_cfg = cookie->get_cfghandle(unesc_domain);
if ((err = SLPOpen(getlocale(), SLP_FALSE, &persrv_h)) != SLP_OK) {
if (current_admin.debug_level >= DBG_NETLOOKUPS) {
(void) logit("update_config: SLPOpen failed: %s\n",
slp_strerror(err));
}
goto cleanup;
}
cookie->h = persrv_h;
if (current_admin.debug_level >= DBG_ALL) {
(void) logit("discovery: found naming context %s\n", context);
}
search = malloc(strlen(cookie->context_attr) +
strlen(context) +
strlen("(=)") + 1);
if (!search) {
(void) logit("update_config: no memory\n");
goto cleanup;
}
(void) sprintf(search, "(%s=%s)", cookie->context_attr, context);
if ((err = SLPOpen(getlocale(), SLP_FALSE, &h)) != SLP_OK) {
if (current_admin.debug_level >= DBG_NETLOOKUPS) {
(void) logit("upate_config: SLPOpen failed: %s\n",
slp_strerror(err));
}
goto cleanup;
}
err = SLPFindSrvs(h, cookie->type, cookie->scopes,
search, foreach_server, cookie);
if (err != SLP_OK) {
if (current_admin.debug_level >= DBG_NETLOOKUPS) {
(void) logit("update_config: SLPFindSrvs failed: %s\n",
slp_strerror(err));
}
goto cleanup;
}
cookie->set_cfghandle(cookie->cache_cfg);
cleanup:
if (h) SLPClose(h);
if (persrv_h) SLPClose(persrv_h);
if (search) free(search);
if (unesc_domain) free(unesc_domain);
}
static SLPBoolean foreach_context(SLPHandle h, const char *attrs_in,
SLPError err, void *cookie) {
char *attrs, *tag, *val, *state;
int type = 0;
if (err != SLP_OK) {
return (SLP_TRUE);
}
attrs = strdup(attrs_in);
if (!attrs) {
(void) logit("foreach_context: no memory\n");
return (SLP_FALSE);
}
state = attrs;
while (next_attr(&tag, &val, &state, &type)) {
update_config(val, cookie);
}
free(attrs);
return (SLP_TRUE);
}
static void find_all_contexts(const char *type,
void *(*get_cfghandle)(const char *),
void (*aggregate)(
void *, const char *, const char *),
void (*set_cfghandle)(void *)) {
SLPHandle h = NULL;
SLPError err;
struct config_cookie cookie[1];
char *fulltype = NULL;
char *scope = (char *)SLPGetProperty("net.slp.useScopes");
if (!scope || !*scope) {
scope = "default";
}
fulltype = malloc(strlen(ABSTYPE) + strlen(type) + 2);
if (!fulltype) {
(void) logit("find_all_contexts: no memory");
goto done;
}
(void) sprintf(fulltype, "%s:%s", ABSTYPE, type);
memset(cookie, 0, sizeof (*cookie));
cookie->type = fulltype;
cookie->scopes = scope;
if (strcasecmp(type, "ldap") == 0) {
cookie->context_attr = LDAP_DOMAIN_ATTR;
} else {
cookie->context_attr = CONTEXT_ATTR;
}
cookie->get_cfghandle = get_cfghandle;
cookie->aggregate = aggregate;
cookie->set_cfghandle = set_cfghandle;
if ((err = SLPOpen(getlocale(), SLP_FALSE, &h)) != SLP_OK) {
if (current_admin.debug_level >= DBG_CANT_FIND) {
(void) logit("discover: %s",
"Aborting discovery: SLPOpen failed: %s\n",
slp_strerror(err));
}
goto done;
}
err = SLPFindAttrs(h, fulltype, scope, cookie->context_attr,
foreach_context, cookie);
if (err != SLP_OK) {
if (current_admin.debug_level >= DBG_CANT_FIND) {
(void) logit(
"discover: Aborting discovery: SLPFindAttrs failed: %s\n",
slp_strerror(err));
}
goto done;
}
done:
if (h) SLPClose(h);
if (fulltype) free(fulltype);
}
void
discover(void *r) {
unsigned short reqrefresh = *((unsigned int *)r);
(void) pthread_setname_np(pthread_self(), "discover");
for (;;) {
find_all_contexts("ldap",
__cache_get_cfghandle,
__cache_aggregate_params,
__cache_set_cfghandle);
if (current_admin.debug_level >= DBG_ALL) {
(void) logit(
"dynamic discovery: using refresh interval %d\n",
reqrefresh);
}
(void) sleep(reqrefresh);
}
}
#endif