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 <stdlib.h>
30 #include <syslog.h>
31 #include <slp-internal.h>
32 
33 struct attr_node {
34 	char *tag, *val;
35 };
36 
37 static SLPError slp_packAttrRqst(slp_handle_impl_t *, const char *,
38 					const char *);
39 static int compare_tags(const void *, const void *);
40 static void collate_attrs(char *, void **, int *, int);
41 static void parens_attr(char *, void **, int *);
42 static void merge_attrs(struct attr_node *, char *);
43 static char *build_attrs_list(void *collator);
44 static void collect_attrs(void *, VISIT, int, void *);
45 static SLPBoolean unpackDAAdvert_attr(slp_handle_impl_t *, char *,
46 					SLPAttrCallback, void *,
47 					void **, int *);
48 static SLPBoolean unpackSAAdvert_attr(slp_handle_impl_t *, char *,
49 					SLPAttrCallback, void *,
50 					void **, int *);
51 
52 SLPError SLPFindAttrs(SLPHandle hSLP, const char *pcURL, const char *pcScope,
53 			const char *pcAttrIds,
54 			SLPAttrCallback callback, void *pvUser) {
55 	SLPError err;
56 	int wantSAAdvert =
57 		strcasecmp(pcURL, "service:service-agent") == 0;
58 	int wantDAAdvert =
59 		strcasecmp(pcURL, "service:directory-agent") == 0;
60 	int isSpecial = wantSAAdvert || wantDAAdvert;
61 	SLPMsgReplyCB *unpack_cb;
62 
63 
64 	if (!hSLP || !pcURL || !pcScope || (!*pcScope && !isSpecial) ||
65 	    !pcAttrIds || !callback) {
66 		return (SLP_PARAMETER_BAD);
67 	}
68 
69 	if ((strlen(pcURL) > SLP_MAX_STRINGLEN) ||
70 	    (strlen(pcScope) > SLP_MAX_STRINGLEN) ||
71 	    (strlen(pcAttrIds) > SLP_MAX_STRINGLEN)) {
72 	    return (SLP_PARAMETER_BAD);
73 	}
74 
75 	if ((err = slp_start_call(hSLP)) != SLP_OK)
76 		return (err);
77 
78 	/* Special packer and unpacker for DA and SA solicitations */
79 	if (wantDAAdvert) {
80 		unpack_cb = (SLPMsgReplyCB *)unpackDAAdvert_attr;
81 		err = slp_packSrvRqst(pcURL, "", hSLP);
82 		((slp_handle_impl_t *)hSLP)->force_multicast = SLP_TRUE;
83 	} else if (wantSAAdvert) {
84 		unpack_cb = (SLPMsgReplyCB *)unpackSAAdvert_attr;
85 		err = slp_packSrvRqst(pcURL, "", hSLP);
86 		((slp_handle_impl_t *)hSLP)->force_multicast = SLP_TRUE;
87 	} else {
88 		/* normal service request */
89 		unpack_cb = (SLPMsgReplyCB *)slp_UnpackAttrReply;
90 		/* format params into msgBuf */
91 		err = slp_packAttrRqst(hSLP, pcURL, pcAttrIds);
92 	}
93 
94 	if (err == SLP_OK)
95 		err = slp_ua_common(hSLP, pcScope,
96 				    (SLPGenericAppCB *) callback, pvUser,
97 				    unpack_cb);
98 
99 	if (err != SLP_OK)
100 		slp_end_call(hSLP);
101 
102 	return (err);
103 }
104 
105 SLPBoolean slp_UnpackAttrReply(slp_handle_impl_t *hp, char *reply,
106 				SLPAttrCallback cb, void *cookie,
107 				void **collator, int *numResults) {
108 	char *pcAttrList;
109 	SLPError errCode;
110 	unsigned short protoErrCode;
111 	size_t len, off;
112 	int maxResults = slp_get_maxResults();
113 	SLPBoolean cont = SLP_TRUE;
114 	int auth_cnt;
115 	size_t tbv_len;
116 	char *attr_tbv;
117 
118 	if (!reply) {
119 		/* no more results */
120 		if (!hp->async) {
121 		    pcAttrList = build_attrs_list(*collator);
122 		}
123 
124 		if (!hp->async && pcAttrList) {
125 		    cb(hp, pcAttrList, SLP_OK, cookie);
126 		    free(pcAttrList);
127 		}
128 		cb(hp, NULL, SLP_LAST_CALL, cookie);
129 		return (SLP_FALSE);
130 	}
131 
132 	/* parse reply into params */
133 	len = slp_get_length(reply);
134 	off = SLP_HDRLEN + slp_get_langlen(reply);
135 	/* err code */
136 	if (slp_get_sht(reply, len, &off, &protoErrCode) != SLP_OK)
137 		return (SLP_TRUE);
138 	/* internal errors should have been filtered out by the net code */
139 	if ((errCode = slp_map_err(protoErrCode)) != SLP_OK) {
140 		return (cb(hp, NULL, errCode, cookie));
141 	}
142 
143 	/* attr list */
144 	attr_tbv = reply + off;
145 	tbv_len = off;
146 	if (slp_get_string(reply, len, &off, &pcAttrList) != SLP_OK)
147 		return (SLP_TRUE);
148 	tbv_len = off - tbv_len;
149 
150 	/* number of attr auths */
151 	if (slp_get_byte(reply, len, &off, &auth_cnt) != SLP_OK) {
152 	    goto cleanup;
153 	}
154 
155 	/* get and verify auth blocks */
156 	if ((!hp->internal_call && slp_get_security_on()) || auth_cnt > 0) {
157 		size_t abLen = 0;
158 		struct iovec iov[1];
159 
160 		iov[0].iov_base = attr_tbv;
161 		iov[0].iov_len = tbv_len;
162 
163 		if (slp_verify(iov, 1,
164 				reply + off,
165 				len - off,
166 				auth_cnt,
167 				&abLen) != SLP_OK) {
168 		    goto cleanup;
169 		}
170 	}
171 
172 	/* collate */
173 	if (!hp->async) {
174 		collate_attrs(pcAttrList, collator, numResults, maxResults);
175 	} else {
176 		/* async: invoke cb */
177 		cont = cb((SLPHandle) hp, pcAttrList, errCode, cookie);
178 		(*numResults)++;
179 	}
180 
181 cleanup:
182 	free(pcAttrList);
183 
184 	/* check maxResults */
185 	if (!hp->internal_call && *numResults == maxResults) {
186 		return (SLP_FALSE);
187 	}
188 
189 	return (cont);
190 }
191 
192 /*
193  * unpackDAAdvert_attr follows the same logic stream as UnpackAttrReply,
194  * except that reply contains a DAAdvert.
195  */
196 static SLPBoolean unpackDAAdvert_attr(slp_handle_impl_t *hp, char *reply,
197 					SLPAttrCallback cb, void *cookie,
198 					void **collator, int *numResults) {
199 	char *surl, *scopes, *attrs, *spis;
200 	SLPBoolean cont = SLP_TRUE;
201 	SLPError errCode;
202 	int maxResults = slp_get_maxResults();
203 
204 	if (!reply) {
205 		/* no more results */
206 		if (!hp->async) {
207 		    attrs = build_attrs_list(*collator);
208 		}
209 
210 		if (!hp->async && attrs) {
211 			cb(hp, attrs, SLP_OK, cookie);
212 			free(attrs);
213 		}
214 		cb(hp, NULL, SLP_LAST_CALL, cookie);
215 		return (SLP_FALSE);
216 	}
217 
218 	if (slp_unpackDAAdvert(reply, &surl, &scopes, &attrs, &spis, &errCode)
219 	    != SLP_OK) {
220 		return (SLP_TRUE);
221 	}
222 	if (errCode != SLP_OK) {
223 		return (cb(hp, NULL, errCode, cookie));
224 	}
225 
226 	/* collate */
227 	if (!hp->async) {
228 		collate_attrs(attrs, collator, numResults, maxResults);
229 	} else {
230 		/* async: invoke cb */
231 		cont = cb((SLPHandle) hp, attrs, errCode, cookie);
232 		(*numResults)++;
233 	}
234 
235 	/* cleanup */
236 	free(surl);
237 	free(scopes);
238 	free(attrs);
239 	free(spis);
240 
241 	/* check maxResults */
242 	if (!hp->internal_call && *numResults == maxResults) {
243 		return (SLP_FALSE);
244 	}
245 
246 	return (cont);
247 }
248 
249 /*
250  * unpackSAAdvert_attr follows the same logic stream as UnpackAttrReply,
251  * except that reply contains an SAAdvert.
252  */
253 static SLPBoolean unpackSAAdvert_attr(slp_handle_impl_t *hp, char *reply,
254 					SLPAttrCallback cb, void *cookie,
255 					void **collator, int *numResults) {
256 	char *surl, *scopes, *attrs;
257 	SLPBoolean cont = SLP_TRUE;
258 	int maxResults = slp_get_maxResults();
259 
260 	if (!reply) {
261 		/* no more results */
262 		if (!hp->async) {
263 		    attrs = build_attrs_list(*collator);
264 		}
265 
266 		if (!hp->async && attrs) {
267 			cb(hp, attrs, SLP_OK, cookie);
268 			free(attrs);
269 		}
270 		cb(hp, NULL, SLP_LAST_CALL, cookie);
271 		return (SLP_FALSE);
272 	}
273 
274 	if (slp_unpackSAAdvert(reply, &surl, &scopes, &attrs) != SLP_OK) {
275 		return (SLP_TRUE);
276 	}
277 
278 	/* collate */
279 	if (!hp->async) {
280 		collate_attrs(attrs, collator, numResults, maxResults);
281 	} else {
282 		/* async: invoke cb */
283 		cont = cb((SLPHandle) hp, attrs, SLP_OK, cookie);
284 		(*numResults)++;
285 	}
286 
287 	/* cleanup */
288 	free(surl);
289 	free(scopes);
290 	free(attrs);
291 
292 	/* check maxResults */
293 	if (!hp->internal_call && *numResults == maxResults) {
294 		return (SLP_FALSE);
295 	}
296 
297 	return (cont);
298 }
299 
300 static SLPError slp_packAttrRqst(slp_handle_impl_t *hp, const char *url,
301 					const char *ids) {
302 	SLPError err;
303 	size_t len, tmplen, msgLen;
304 	slp_msg_t *msg = &(hp->msg);
305 	char *spi = NULL;
306 
307 	if (slp_get_security_on()) {
308 	    spi = (char *)SLPGetProperty(SLP_CONFIG_SPI);
309 	}
310 
311 	if (!spi || !*spi) {
312 		spi = "";
313 	}
314 
315 	/*
316 	 * Allocate iovec for the messge. An AttrRqst is layed out thus:
317 	 *  0: header
318 	 *  1: prlist length
319 	 *  2: prlist (filled in later by networking code)
320 	 *  3: URL string
321 	 *  4: scopes length
322 	 *  5: scopes (filled in later by networking code)
323 	 *  6: tag list string and SPI string
324 	 */
325 	if (!(msg->iov = calloc(7, sizeof (*(msg->iov))))) {
326 		slp_err(LOG_CRIT, 0, "slp_packAttrRqst", "out of memory");
327 		return (SLP_MEMORY_ALLOC_FAILED);
328 	}
329 	msg->iovlen = 7;
330 
331 	/* calculate msg length */
332 	msgLen = 2 +		/* prlist length */
333 	    2 + strlen(url) +	/* URL */
334 	    2 +			/* scope list length */
335 	    2 + strlen(ids) +	/* tag list */
336 	    2 + strlen(spi);	/* SPI string */
337 
338 	if (!(msg->msg = calloc(1, msgLen))) {
339 		free(msg->iov);
340 		slp_err(LOG_CRIT, 0, "slp_packAttrRqst", "out of memory");
341 		return (SLP_MEMORY_ALLOC_FAILED);
342 	}
343 
344 	/* set pointer to PR list and scope list length spaces */
345 	msg->prlistlen.iov_base = msg->msg;
346 	msg->prlistlen.iov_len = 2;
347 	msg->iov[1].iov_base = msg->msg;
348 	msg->iov[1].iov_len = 2;
349 
350 	msg->scopeslen.iov_base = msg->msg + 2;
351 	msg->scopeslen.iov_len = 2;
352 	msg->iov[4].iov_base = msg->msg + 2;
353 	msg->iov[4].iov_len = 2;
354 
355 	/* set up the scopes and prlist pointers into iov */
356 	msg->prlist = &(msg->iov[2]);
357 	msg->scopes = &(msg->iov[5]);
358 
359 	len = 4;
360 
361 	/* Add URL string */
362 	msg->iov[3].iov_base = msg->msg + len;
363 	tmplen = len;
364 
365 	err = slp_add_string(msg->msg, msgLen, url, &len);
366 	msg->iov[3].iov_len = len - tmplen;
367 
368 	if (err != SLP_OK)
369 		goto error;
370 
371 	/* Add tag list */
372 	msg->iov[6].iov_base = msg->msg + len;
373 	tmplen = len;
374 
375 	err = slp_add_string(msg->msg, msgLen, ids, &len);
376 
377 	if (err != SLP_OK)
378 		goto error;
379 
380 	/* SPI string */
381 	err = slp_add_string(msg->msg, msgLen, spi, &len);
382 
383 	msg->iov[6].iov_len = len - tmplen;
384 
385 	hp->fid = ATTRRQST;
386 	if (err == SLP_OK) {
387 		return (SLP_OK);
388 	}
389 
390 	/* else error */
391 error:
392 	free(msg->iov);
393 	free(msg->msg);
394 
395 	return (err);
396 }
397 
398 SLPError slp_packAttrRqst_single(const char *url,
399 				const char *scopes,
400 				const char *ids,
401 				char **msg,
402 				const char *lang) {
403 	SLPError err;
404 	size_t len, msgLen;
405 
406 	msgLen =
407 		SLP_HDRLEN + strlen(lang) + 2 +
408 		2 + strlen(url) +
409 		2 + strlen(scopes) +
410 		2 + strlen(ids) +
411 		2; /* No SPI string for internal calls */
412 
413 	if (!(*msg = calloc(msgLen, 1))) {
414 	    slp_err(LOG_CRIT, 0, "slp_packAttrRqst_single", "out of memory");
415 	    return (SLP_MEMORY_ALLOC_FAILED);
416 	}
417 
418 	len = 0;
419 	err = slp_add_header(lang, *msg, msgLen, ATTRRQST, msgLen, &len);
420 
421 	len += 2;	/* empty PR list */
422 
423 	if (err == SLP_OK) {
424 	    err = slp_add_string(*msg, msgLen, url, &len);
425 	}
426 	if (err == SLP_OK) {
427 	    err = slp_add_string(*msg, msgLen, scopes, &len);
428 	}
429 	if (err == SLP_OK) {
430 	    err = slp_add_string(*msg, msgLen, ids, &len);
431 	}
432 	/* empty SPI */
433 	if (err == SLP_OK) {
434 	    err = slp_add_string(*msg, msgLen, "", &len);
435 	}
436 
437 	return (err);
438 }
439 
440 static int compare_tags(const void *n1, const void *n2) {
441 	return slp_strcasecmp(
442 		((struct attr_node *)n1)->tag,
443 		((struct attr_node *)n2)->tag);
444 }
445 
446 static void merge_attrs(struct attr_node *n, char *vals) {
447 	char *p, *v;
448 
449 	for (p = v = vals; p; v = p) {
450 		p = slp_utf_strchr(v, ',');
451 		if (p)
452 			*p++ = 0;
453 		slp_add2list(v, &(n->val), SLP_TRUE);
454 	}
455 }
456 
457 static void parens_attr(char *attr, void **collator, int *numResults) {
458 	char *open_paren, *close_paren, *equals;
459 	struct attr_node *n, **res;
460 
461 	open_paren = attr + 1;
462 	close_paren = slp_utf_strchr(open_paren, ')');
463 	if (!close_paren)
464 		return;	/* skip bad attr list */
465 
466 	*close_paren = 0;
467 	if (!(equals = slp_utf_strchr(open_paren, '=')))
468 		return;
469 
470 	*equals++ = 0;
471 
472 	if (!(n = malloc(sizeof (*n)))) {
473 		slp_err(LOG_CRIT, 0, "collate_attrs", "out of memory");
474 		return;
475 	}
476 
477 	if (!(n->tag = strdup(open_paren))) {
478 		free(n);
479 		slp_err(LOG_CRIT, 0, "collate_attrs", "out of memory");
480 		return;
481 	}
482 	n->val = NULL;
483 
484 	res = slp_tsearch(n, collator, compare_tags);
485 
486 	if (*res != n) {
487 		merge_attrs(*res, equals);
488 		free(n->tag); free(n);
489 	} else {
490 		/* not found; populate new attr node */
491 		(*numResults)++;
492 		if (!(n->val = strdup(equals))) {
493 			slp_err(LOG_CRIT, 0, "collate_attrs", "out of memory");
494 			return;
495 		}
496 	}
497 }
498 
499 static void collate_attrs(char *attrs, void **collator,
500 				int *numResults, int maxResults) {
501 	char *start, *end;
502 	struct attr_node *n, **res;
503 
504 	for (start = attrs;
505 			start &&
506 			*start &&
507 			*numResults != maxResults;
508 						start = end) {
509 		if (*start == ',') start++;
510 		if (*start == '(') {
511 			/* form of (tag=val,val) */
512 			if (!(end = slp_utf_strchr(start, ')')))
513 				return;		/* skip bad attr */
514 			parens_attr(start, collator, numResults);
515 			end++;
516 			continue;
517 		}
518 		end = slp_utf_strchr(start, ',');
519 		if (end)
520 			*end++ = 0;
521 		/* create a new node with the tag only */
522 		if (!(n = malloc(sizeof (*n)))) {
523 			slp_err(LOG_CRIT, 0, "collate_attrs", "out of memory");
524 			return;
525 		}
526 
527 		if (!(n->tag = strdup(start))) {
528 			free(n);
529 			slp_err(LOG_CRIT, 0, "collate_attrs", "out of memory");
530 			return;
531 		}
532 		n->val = NULL;
533 		res = slp_tsearch(n, collator, compare_tags);
534 		if (*res != n) {
535 			/* already in the tree, so just free resources */
536 			free(n->tag); free(n);
537 		}
538 		(*numResults)++;
539 	}
540 }
541 
542 static char *build_attrs_list(void *collator) {
543 	char *answer = NULL;
544 
545 	if (!collator)
546 		return (NULL);
547 
548 	slp_twalk(collator, collect_attrs, 0, &answer);
549 	return (answer);
550 }
551 
552 /*ARGSUSED*/
553 static void collect_attrs(void *node, VISIT order, int level, void *cookie) {
554 	struct attr_node *n;
555 	char *attr, *p, **answer = (char **)cookie;
556 
557 	if (order == endorder || order == leaf) {
558 		n = *(struct attr_node **)node;
559 		if (!n->val) {
560 			/* no values, so no parens */
561 			if (!(attr = malloc(strlen(n->tag) + 1))) {
562 				slp_err(LOG_CRIT, 0, "collect_attrs",
563 					"out of memory");
564 				return;
565 			}
566 			(void) strcpy(attr, n->tag);
567 		} else {
568 			if (!(attr = malloc(1 + strlen(n->tag) + 1 +
569 					    strlen(n->val) + 2))) {
570 				slp_err(LOG_CRIT, 0, "collect_attrs",
571 					"out of memory");
572 				return;
573 			}
574 			/* build attr string */
575 			p = attr;
576 			*p++ = '(';
577 			(void) strcpy(p, n->tag); p += strlen(n->tag);
578 			*p++ = '=';
579 			(void) strcpy(p, n->val); p += strlen(n->val);
580 			*p++ = ')'; *p = 0;
581 		}
582 
583 		slp_add2list(attr, answer, SLP_FALSE);
584 		free(attr);
585 		free(n->tag); if (n->val) free(n->val); free(n);
586 		free(node);
587 	}
588 }
589