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