xref: /illumos-gate/usr/src/lib/libslp/clib/SLPFindSrvs.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <syslog.h>
30 #include <slp-internal.h>
31 
32 struct surl_node {
33 	char *surl;
34 	unsigned short lifetime;
35 };
36 
37 struct caller_bundle {
38 	SLPSrvURLCallback *cb;
39 	void *cookie;
40 	SLPHandle handle;
41 };
42 
43 static int compare_surls(struct surl_node *, struct surl_node *);
44 static char *collate_surls(char *, unsigned short, void **);
45 static void traverse_surls(SLPHandle, SLPSrvURLCallback, void *, void *);
46 static void process_surl_node(void *, VISIT, int, void *);
47 static SLPBoolean unpackDAAdvert_srv(slp_handle_impl_t *, char *,
48 					SLPSrvURLCallback, void *,
49 					void **, int *);
50 static SLPBoolean unpackSAAdvert_srv(slp_handle_impl_t *, char *,
51 					SLPSrvURLCallback, void *,
52 					void **, int *);
53 
54 SLPError SLPFindSrvs(SLPHandle hSLP, const char *pcServiceType,
55 			const char *pcScope, const char *pcSearchFilter,
56 			SLPSrvURLCallback callback, void *pvUser) {
57 	SLPError err;
58 	slp_handle_impl_t *hp = (slp_handle_impl_t *)hSLP;
59 	int wantSAAdvert =
60 		strcasecmp(pcServiceType, "service:service-agent") == 0;
61 	int wantDAAdvert =
62 		strcasecmp(pcServiceType, "service:directory-agent") == 0;
63 	int isSpecial = wantSAAdvert || wantDAAdvert;
64 	SLPMsgReplyCB *unpack_cb;
65 
66 	if (!hSLP || !pcServiceType || !pcScope || (!*pcScope && !isSpecial) ||
67 	    !pcSearchFilter || !callback) {
68 		return (SLP_PARAMETER_BAD);
69 	}
70 
71 	if ((strlen(pcServiceType) > SLP_MAX_STRINGLEN) ||
72 	    (strlen(pcScope) > SLP_MAX_STRINGLEN) ||
73 	    (strlen(pcSearchFilter) > SLP_MAX_STRINGLEN)) {
74 	    return (SLP_PARAMETER_BAD);
75 	}
76 
77 	if ((err = slp_start_call(hSLP)) != SLP_OK)
78 		return (err);
79 
80 	/* Special unpacker for DA and SA solicitations */
81 	if (wantDAAdvert) {
82 		unpack_cb = (SLPMsgReplyCB *)unpackDAAdvert_srv;
83 		hp->force_multicast = SLP_TRUE;
84 	} else if (wantSAAdvert) {
85 		unpack_cb = (SLPMsgReplyCB *)unpackSAAdvert_srv;
86 		hp->force_multicast = SLP_TRUE;
87 	} else {
88 		/* normal service request */
89 		unpack_cb = (SLPMsgReplyCB *)slp_unpackSrvReply;
90 	}
91 
92 	err = slp_packSrvRqst(pcServiceType, pcSearchFilter, hp);
93 
94 	if (err == SLP_OK)
95 		err = slp_ua_common(hSLP, pcScope,
96 		    (SLPGenericAppCB *)(uintptr_t)callback, pvUser, unpack_cb);
97 	if (err != SLP_OK)
98 		slp_end_call(hSLP);
99 
100 	return (err);
101 }
102 
103 SLPBoolean slp_unpackSrvReply(slp_handle_impl_t *hp, char *reply,
104 				SLPSrvURLCallback cb, void *cookie,
105 				void **collator, int *numResults) {
106 	SLPError errCode;
107 	unsigned short urlCount, protoErrCode;
108 	size_t len, off;
109 	int i;
110 	int maxResults = slp_get_maxResults();
111 	SLPBoolean cont = SLP_TRUE;
112 
113 	if (!reply) {
114 		/* no more results */
115 		/* traverse_surls:invoke cb for sync case,and free resources */
116 		if (!hp->async) {
117 		    traverse_surls(hp, cb, cookie, *collator);
118 		}
119 		cb(hp, NULL, 0, SLP_LAST_CALL, cookie);
120 		return (SLP_FALSE);
121 	}
122 
123 	len = slp_get_length(reply);
124 	off = SLP_HDRLEN + slp_get_langlen(reply);
125 	/* err code */
126 	if (slp_get_sht(reply, len, &off, &protoErrCode) != SLP_OK)
127 		return (SLP_TRUE);
128 	/* internal errors should have been filtered out by the net code */
129 	if ((errCode = slp_map_err(protoErrCode)) != SLP_OK) {
130 		return (cb(hp, NULL, 0, errCode, cookie));
131 	}
132 
133 	/* url entry count */
134 	if (slp_get_sht(reply, len, &off, &urlCount) != SLP_OK)
135 		return (SLP_TRUE);
136 
137 	/* for each srvRply, unpack and pass to CB */
138 	for (i = 0; i < urlCount && !hp->cancel; i++) {
139 		char *pcSrvURL;
140 		unsigned short sLifetime;
141 		int nURLAuthBlocks;
142 		size_t tbv_len;
143 		char *url_tbv;
144 
145 		/* parse URL entry into params */
146 		off++;	/* skip reserved byte */
147 		/* lifetime */
148 		if (slp_get_sht(reply, len, &off, &sLifetime) != SLP_OK)
149 			return (SLP_TRUE);
150 		/* URL itself; keep track of it in case we need to verify */
151 		url_tbv = reply + off;
152 		tbv_len = off;
153 		if (slp_get_string(reply, len, &off, &pcSrvURL) != SLP_OK)
154 			return (SLP_TRUE);
155 		tbv_len = off - tbv_len;
156 
157 		/* number of url auths */
158 		if (slp_get_byte(reply, len, &off, &nURLAuthBlocks) != SLP_OK)
159 			goto cleanup;
160 
161 		/* get and verify auth blocks */
162 		if ((!hp->internal_call && slp_get_security_on()) ||
163 		    nURLAuthBlocks > 0) {
164 			struct iovec iov[1];
165 			size_t abLen = 0;
166 
167 			iov[0].iov_base = url_tbv;
168 			iov[0].iov_len = tbv_len;
169 
170 			if (slp_verify(iov, 1,
171 					reply + off,
172 					len - off,
173 					nURLAuthBlocks,
174 					&abLen) != SLP_OK) {
175 			    goto cleanup;
176 			}
177 			off += abLen;
178 		}
179 
180 		/* collate the srv urls for sync behavior */
181 		if (!hp->async) {
182 		    pcSrvURL = collate_surls(pcSrvURL, sLifetime, collator);
183 
184 		    if (!pcSrvURL)
185 			continue;
186 		}
187 
188 		(*numResults)++;
189 		/* invoke cb */
190 		if (hp->async)
191 			cont = cb(
192 				(SLPHandle) hp,
193 				pcSrvURL,
194 				sLifetime,
195 				errCode,
196 				cookie);
197 
198 		/* cleanup */
199 cleanup:
200 		free(pcSrvURL);
201 
202 		/* check maxResults */
203 		if (!hp->internal_call && *numResults == maxResults) {
204 			cont = SLP_FALSE;
205 		}
206 
207 		if (!cont) break;
208 	}
209 
210 	return (cont);
211 }
212 
213 /*
214  * unpackDAAdvert_srv follows the same same logic flow as slp_unpackSrvReply
215  * with two differences: the message in reply is a DAAdvert, and
216  * this function is not used internally, so hp is never NULL. Although
217  * all info from a DAAdvert is returned by slp_unpackDAAdvert, here
218  * the recipient (the user-supplied SLPSrvURLCallback) is interested
219  * only in the DA service URL.
220  */
221 static SLPBoolean unpackDAAdvert_srv(slp_handle_impl_t *hp, char *reply,
222 					SLPSrvURLCallback cb, void *cookie,
223 					void **collator, int *numResults) {
224 	char *surl, *scopes, *attrs, *spis;
225 	SLPBoolean cont = SLP_TRUE;
226 	SLPError errCode;
227 	int maxResults = slp_get_maxResults();
228 
229 	if (!reply) {
230 		/* no more results */
231 		/* traverse_surls:invoke cb for sync case,and free resources */
232 		if (!hp->async) {
233 			traverse_surls(hp, cb, cookie, *collator);
234 		}
235 		cb(hp, NULL, 0, SLP_LAST_CALL, cookie);
236 		return (SLP_FALSE);
237 	}
238 
239 	if (slp_unpackDAAdvert(reply, &surl, &scopes, &attrs, &spis, &errCode)
240 	    != SLP_OK) {
241 		return (SLP_TRUE);
242 	}
243 	if (errCode != SLP_OK) {
244 		return (cb(hp, NULL, 0, errCode, cookie));
245 	}
246 
247 	/* collate the urls */
248 	surl = collate_surls(surl, 0, collator);
249 	if (!surl) {
250 		return (SLP_TRUE);
251 	}
252 
253 	(*numResults)++;
254 	if (hp->async) {
255 		cont = cb((SLPHandle)hp, surl, 0, errCode, cookie);
256 	}
257 
258 	/* cleanup */
259 	free(surl);
260 	free(scopes);
261 	free(attrs);
262 	free(spis);
263 
264 	/* check maxResults */
265 	if (!hp->internal_call && *numResults == maxResults) {
266 		return (SLP_FALSE);
267 	}
268 
269 	return (cont);
270 }
271 /*
272  * unpackSAAdvert_srv follows the same same logic flow as slp_unpackSrvReply
273  * with two differences: the message in reply is a SAAdvert, and
274  * this function is not used internally, so hp is never NULL. Although
275  * all info from an SAAdvert is returned by slp_unpackSAAdvert, here
276  * the recipient (the user-supplied SLPSrvURLCallback) is interested
277  * only in the SA service URL.
278  */
279 static SLPBoolean unpackSAAdvert_srv(slp_handle_impl_t *hp, char *reply,
280 					SLPSrvURLCallback cb, void *cookie,
281 					void **collator, int *numResults) {
282 	char *surl, *scopes, *attrs;
283 	SLPBoolean cont = SLP_TRUE;
284 	int maxResults = slp_get_maxResults();
285 
286 	if (!reply) {
287 		/* no more results */
288 		/* traverse_surls:invoke cb for sync case,and free resources */
289 		if (!hp->async) {
290 			/* sync case */
291 			traverse_surls(hp, cb, cookie, *collator);
292 		}
293 		cb(hp, NULL, 0, SLP_LAST_CALL, cookie);
294 		return (SLP_FALSE);
295 	}
296 
297 	if (slp_unpackSAAdvert(reply, &surl, &scopes, &attrs) != SLP_OK) {
298 		return (SLP_TRUE);
299 	}
300 
301 	/* collate the urls */
302 	surl = collate_surls(surl, 0, collator);
303 	if (!surl) {
304 		return (SLP_TRUE);
305 	}
306 
307 	(*numResults)++;
308 	if (hp->async) {
309 		cont = cb((SLPHandle)hp, surl, 0, SLP_OK, cookie);
310 	}
311 
312 	/* cleanup */
313 	free(surl);
314 	free(scopes);
315 	free(attrs);
316 
317 	/* check maxResults */
318 	if (!hp->internal_call && *numResults == maxResults) {
319 		return (SLP_FALSE);
320 	}
321 
322 	return (cont);
323 }
324 
325 SLPError slp_packSrvRqst(const char *type,
326 				const char *filter,
327 				slp_handle_impl_t *hp) {
328 	SLPError err;
329 	size_t len, msgLen, tmplen;
330 	slp_msg_t *msg = &(hp->msg);
331 	char *spi = NULL;
332 
333 	if (slp_get_security_on()) {
334 	    spi = (char *)SLPGetProperty(SLP_CONFIG_SPI);
335 	}
336 
337 	if (!spi || !*spi) {
338 		spi = "";
339 	}
340 
341 	/*
342 	 * Allocate iovec for the messge. A SrvRqst is layed out thus:
343 	 *  0: header
344 	 *  1: prlist length
345 	 *  2: prlist (filled in later by networking code)
346 	 *  3: service type string
347 	 *  4: scopes length
348 	 *  5: scopes (filled in later by networking code)
349 	 *  6: predicate string and SPI string
350 	 */
351 	if (!(msg->iov = calloc(7, sizeof (*(msg->iov))))) {
352 		slp_err(LOG_CRIT, 0, "slp_packSrvRqst", "out of memory");
353 		return (SLP_MEMORY_ALLOC_FAILED);
354 	}
355 	msg->iovlen = 7;
356 
357 	/* calculate msg length */
358 	msgLen = 2 +		/* prlist length */
359 	    2 + strlen(type) +	/* service type */
360 	    2 +			/* scope list length */
361 	    2 + strlen(filter) + /* predicate string */
362 	    2 + strlen(spi);	/* SPI string */
363 
364 	if (!(msg->msg = calloc(1, msgLen))) {
365 		free(msg->iov);
366 		slp_err(LOG_CRIT, 0, "slp_packSrvRqst", "out of memory");
367 		return (SLP_MEMORY_ALLOC_FAILED);
368 	}
369 
370 	/* set pointer to PR list and scope list length spaces */
371 	msg->prlistlen.iov_base = msg->msg;
372 	msg->prlistlen.iov_len = 2;
373 	msg->iov[1].iov_base = msg->msg;
374 	msg->iov[1].iov_len = 2;
375 
376 	msg->scopeslen.iov_base = msg->msg + 2;
377 	msg->scopeslen.iov_len = 2;
378 	msg->iov[4].iov_base = msg->msg + 2;
379 	msg->iov[4].iov_len = 2;
380 
381 	/* set up the scopes and prlist pointers into iov */
382 	msg->prlist = &(msg->iov[2]);
383 	msg->scopes = &(msg->iov[5]);
384 
385 	len = 4;
386 
387 	/* Add type string */
388 	msg->iov[3].iov_base = msg->msg + len;
389 	tmplen = len;
390 
391 	err = slp_add_string(msg->msg, msgLen, type, &len);
392 	msg->iov[3].iov_len = len - tmplen;
393 
394 	if (err != SLP_OK)
395 		goto error;
396 
397 	/* Add search filter */
398 	msg->iov[6].iov_base = msg->msg + len;
399 	tmplen = len;
400 
401 	err = slp_add_string(msg->msg, msgLen, filter, &len);
402 	if (err != SLP_OK)
403 		goto error;
404 
405 	err = slp_add_string(msg->msg, msgLen, spi, &len);
406 
407 	msg->iov[6].iov_len = len - tmplen;
408 
409 	hp->fid = SRVRQST;
410 
411 	if (err == SLP_OK) {
412 		return (err);
413 	}
414 
415 	/* else error */
416 error:
417 	free(msg->iov);
418 	free(msg->msg);
419 
420 	return (err);
421 }
422 
423 /*
424  * Caller must free msg
425  */
426 SLPError slp_packSrvRqst_single(const char *type,
427 				const char *scopes,
428 				const char *filter,
429 				char **msg,
430 				const char *lang) {
431 	SLPError err;
432 	size_t len, msgLen;
433 
434 	msgLen =
435 		SLP_HDRLEN + strlen(lang) + 2 +
436 		2 + strlen(type) +
437 		2 + strlen(scopes) +
438 		2 + strlen(filter) +
439 		2; /* No SPI string for internal calls */
440 
441 	if (!(*msg = calloc(msgLen, 1))) {
442 		slp_err(LOG_CRIT, 0, "slp_packSrvRqst_single",
443 			"out of memory");
444 		return (SLP_MEMORY_ALLOC_FAILED);
445 	}
446 
447 	len = 0;
448 	err = slp_add_header(lang, *msg, msgLen, SRVRQST, msgLen, &len);
449 
450 	len += 2;	/* empty PR list */
451 
452 	if (err == SLP_OK)
453 		err = slp_add_string(*msg, msgLen, type, &len);
454 	if (err == SLP_OK)
455 		err = slp_add_string(*msg, msgLen, scopes, &len);
456 	if (err == SLP_OK)
457 		err = slp_add_string(*msg, msgLen, filter, &len);
458 	if (err == SLP_OK)
459 		/* empty SPI string */
460 		err = slp_add_string(*msg, msgLen, "", &len);
461 
462 	return (err);
463 }
464 
465 
466 static int compare_surls(struct surl_node *s1, struct surl_node *s2) {
467 	if (s1->lifetime != s2->lifetime)
468 		return (s1->lifetime - s2->lifetime);
469 	return (slp_strcasecmp(s1->surl, s2->surl));
470 }
471 
472 /*
473  * Using the collator, determine if this URL has already been processed.
474  * If so, free surl and return NULL, else return the URL.
475  */
476 static char *collate_surls(char *surl, unsigned short life, void **collator) {
477 	struct surl_node *n, **res;
478 
479 	if (!(n = malloc(sizeof (*n)))) {
480 		slp_err(LOG_CRIT, 0, "collate_surls", "out of memory");
481 		return (NULL);
482 	}
483 	if (!(n->surl = strdup(surl))) {
484 		free(n);
485 		slp_err(LOG_CRIT, 0, "collate_surls", "out of memory");
486 		return (NULL);
487 	}
488 	n->lifetime = life;
489 	res = slp_tsearch((void *) n, collator,
490 			(int (*)(const void *, const void *)) compare_surls);
491 	if (*res == n) {
492 		/* first time we've encountered this url */
493 		return (surl);
494 	}
495 	/* else  already in tree */
496 	free(n->surl);
497 	free(n);
498 	free(surl);
499 	return (NULL);
500 }
501 
502 static void traverse_surls(SLPHandle h, SLPSrvURLCallback cb,
503 				void *cookie, void *collator) {
504 	struct caller_bundle caller[1];
505 
506 	if (!collator)
507 		return;
508 	caller->cb = cb;
509 	caller->cookie = cookie;
510 	caller->handle = h;
511 	slp_twalk(collator, process_surl_node, 0, caller);
512 }
513 
514 /*ARGSUSED*/
515 static void process_surl_node(void *node, VISIT order, int level, void *c) {
516 	struct surl_node *n;
517 	SLPSrvURLCallback *cb;
518 	slp_handle_impl_t *h;
519 	struct caller_bundle *caller = (struct caller_bundle *)c;
520 
521 	if (order == endorder || order == leaf) {
522 		SLPBoolean cont = SLP_TRUE;
523 
524 		cb = caller->cb;
525 		h = (slp_handle_impl_t *)caller->handle;
526 		n = *(struct surl_node **)node;
527 		/* invoke cb */
528 		if (cont && (!h || !h->async))
529 			cont = cb(
530 				h, n->surl,
531 				n->lifetime,
532 				SLP_OK,
533 				caller->cookie);
534 
535 		free(n->surl);
536 		free(n);
537 		free(node);
538 	}
539 }
540