xref: /illumos-gate/usr/src/lib/libslp/clib/slp_utils.c (revision 069e6b7e31ba5dcbc5441b98af272714d9a5455c)
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 /*
30  * Miscellaneous Utilities
31  *
32  * slp_err:		Error and information message dispatch, i18n'd
33  * slp_start_call:	Marks a SLP handle as in-use
34  * slp_end_call:	Marks a SLP handle as available
35  * slp_map_err:		protocol to API error mapping
36  * slp_onlist:		determines if a token is on a list
37  * slp_add2list:	adds a token to a list
38  * slp_list_subtract:	removes a token from a list
39  * slp_add_header:	creates a SLP message header
40  * slp_get_length:	gets the length field from a SLP header
41  * slp_set_length:	sets the length field in a SLP header
42  * slp_header_get_sht:	gets a 16 bit integer from a SLP header
43  * slp_header_set_sht:	sets a 16 bit interger in a SLP header
44  * slp_header_length:	calculates the length of a header, including the
45  *				language tag
46  * slp_get_errcode:	returns the error code from a SLP message
47  * slp_add_byte:	encodes a byte into the given buffer
48  * slp_add_sht:		encodes a 16-bit integer into the given buffer
49  * slp_add_string:	encodes the given string into the given buffer
50  * slp_get_byte:	decodes a byte from the given buffer
51  * slp_get_sht:		decodes a 16-bit integer from the given buffer
52  * slp_get_string:	decodes a string from the given buffer
53  */
54 
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <stdarg.h>
58 #include <syslog.h>
59 #include <string.h>
60 #include <thread.h>
61 #include <synch.h>
62 #include <errno.h>
63 #include <unistd.h>
64 #include <limits.h>
65 #include <arpa/inet.h>
66 #include <libintl.h>
67 #include <slp-internal.h>
68 
69 #define	SLP_ERR_BUF_LEN	1024UL
70 
71 /*
72  * Outputs an error message. priority is a syslog(3) priority.
73  */
74 /*ARGSUSED1*/
75 /* PRINTFLIKE4 */
76 void slp_err(int priority, int id, char *func, char *inmsg, ...) {
77 	static char buf[SLP_ERR_BUF_LEN];
78 	char *p, *msg;
79 	size_t len;
80 	va_list ap;
81 	static mutex_t loglock = DEFAULTMUTEX;
82 	va_start(ap, inmsg);
83 
84 	(void) mutex_lock(&loglock);
85 
86 	/* i18n mapping */
87 	msg = dgettext("libslp", inmsg);
88 
89 	(void) snprintf(buf, sizeof (buf), "libslp: %s: ", func);
90 	len = strlen(buf);
91 	p = &(buf[len]);
92 	(void) vsnprintf(p, SLP_ERR_BUF_LEN - len, msg, ap);
93 	va_end(ap);
94 	syslog(priority, buf);
95 	(void) mutex_unlock(&loglock);
96 }
97 
98 /*
99  * Start and end slp calls
100  * slp_start_call returns SLP_HANDLE_IN_USE if the handle is already
101  * being used, otherwise SLP_OK.
102  */
103 SLPError slp_start_call(slp_handle_impl_t *hp) {
104 	(void) mutex_lock(&(hp->outcall_lock));
105 	if (hp->pending_outcall) {
106 	    (void) mutex_unlock(&(hp->outcall_lock));
107 	    return (SLP_HANDLE_IN_USE);
108 	}
109 	hp->pending_outcall = SLP_TRUE;
110 	(void) mutex_unlock(&(hp->outcall_lock));
111 
112 	hp->cancel = 0;
113 	return (SLP_OK);
114 }
115 
116 void slp_end_call(slp_handle_impl_t *hp) {
117 	(void) mutex_lock(&(hp->outcall_lock));
118 	if (hp->close_on_end) {
119 	    /* SLPClose() called from callback */
120 	    (void) mutex_unlock(&(hp->outcall_lock));
121 	    slp_cleanup_handle(hp);
122 	    return;
123 	}
124 
125 	hp->pending_outcall = SLP_FALSE;
126 	(void) cond_signal(&(hp->outcall_cv));
127 	(void) mutex_unlock(&(hp->outcall_lock));
128 }
129 
130 /*
131  * Map a protocol error code to an API error code.
132  */
133 SLPError slp_map_err(unsigned short proto_err) {
134 	switch (proto_err) {
135 	case 0:	return (SLP_OK);
136 	case 1:	return (SLP_LANGUAGE_NOT_SUPPORTED);
137 	case 2:	return (SLP_PARSE_ERROR);
138 	case 3:	return (SLP_INVALID_REGISTRATION);
139 	case 4:	return (SLP_SCOPE_NOT_SUPPORTED);
140 	case 6:	return (SLP_AUTHENTICATION_ABSENT);
141 	case 7:	return (SLP_AUTHENTICATION_FAILED);
142 	case 13:	return (SLP_INVALID_UPDATE);
143 		/*
144 		 * 9 (VER_NOT_SUPPORTED), 10 (INTERNAL_ERROR),
145 		 * 11 (DA_BUSY_NOW), 12 (OPTION_NOT_UNDERSTOOD),
146 		 * and 14 (RQST_NOT_SUPPORTED)
147 		 * should be handled internally by the API.
148 		 */
149 	default:	return (SLP_INTERNAL_SYSTEM_ERROR);
150 	}
151 }
152 
153 /*
154  * SLP List Management:
155  * SLP lists are comma separated lists of tokens. The following routines
156  * manage SLP lists, ensuring proper UTF-8 parsing.
157  */
158 
159 /*
160  * If 'item' is on 'list', returns 1, otherwise 0.
161  */
162 int slp_onlist(const char *item, const char *list) {
163 	char *p;
164 	for (p = (char *)list; p; p++) {
165 		char *s;
166 		size_t span;
167 
168 		s = p;
169 		p = slp_utf_strchr(p, ',');
170 		span = (p ? (size_t)(p - s): strlen(s));
171 
172 		if (strlen(item) != span) {
173 			if (!p)
174 				break;
175 			else
176 				continue;
177 		}
178 
179 		if (strncasecmp(item, s, span) == 0)
180 			return (1);
181 		if (!p)
182 			break;
183 	}
184 	return (0);
185 }
186 
187 /*
188  * Adds item to *list if it is not already on it. If *list == NULL,
189  * creates a new list. When it grows the list, it will free *list,
190  * so *list must not be on the caller's stack. 'check_onlist' specifies
191  * whether to look to item on the current list. This is a small
192  * optimization for callers which are that item is not on *list, or
193  * which don't care about duplicates.
194  */
195 void slp_add2list(const char *item, char **list, SLPBoolean check_onlist) {
196 	if (!(*list)) {
197 		if (!(*list = strdup(item)))
198 			slp_err(LOG_CRIT, 0, "slp_add2list", "out of memory");
199 		return;
200 	}
201 
202 	if (check_onlist)
203 		/* no duplicates */
204 		if (slp_onlist(item, *list))
205 			return;
206 
207 	if (!(*list = realloc(*list, strlen(*list) + strlen(item) + 2))) {
208 		slp_err(LOG_CRIT, 0, "slp_add2list", "out of memory");
209 		return;
210 	}
211 	(void) strcat(*list, ",");
212 	(void) strcat(*list, item);
213 }
214 
215 /*
216  * Removes the first instance of item from *list.
217  * When it shrinks the list, it may free *list, so *list must not be on
218  * the caller's stack.
219  */
220 void slp_list_subtract(const char *item, char **list) {
221 	char *p, *s;
222 
223 	if (!*list || !slp_onlist(item, *list))
224 		return;
225 	/* find item's location on the list */
226 	for (p = *list; p; p++) {
227 		size_t span;
228 
229 		s = p;
230 		p = slp_utf_strchr(p, ',');
231 		span = (p ? (size_t)(p - s) : strlen(s));
232 		if (strlen(item) != span)
233 			continue;
234 		if (strncasecmp(item, s, span) == 0)
235 			break;
236 		if (!p)
237 			break;
238 	}
239 	if (!p && s == *list) {
240 		/* item is only one on list */
241 		free(*list);
242 		*list = NULL;
243 		return;
244 	}
245 	if (!p) {
246 		/* last one on list; just chop it off */
247 		s--;
248 		*s = 0;
249 		return;
250 	}
251 	/* either first on list, or somewhere in the middle */
252 	(void) strcpy(s, p + 1);
253 }
254 
255 /* SLPv2 header management */
256 
257 /*
258  * Lays a SLP header into pcSendBuf, performing byte-ordering and bounds
259  * checking where necessary.
260  * pcLangTag: Language tag
261  * pcSendBuf: a buffer into which to write the composed header
262  * iSendBufSz: the size of pcSendBuf in bytes
263  * iFun: SLP V2 function number
264  * iLen: The length of the whole SLP message, in bytes
265  * piLen: a pointer to an int into which will be written the size of the
266  *	  header + the language tag (i.e. the offset at which the rest of
267  *	  the message should be written into pcSendBuf).
268  */
269 SLPError slp_add_header(const char *pcLangTag, char *pcSendBuf,
270 			size_t iSendBufSz, int iFun,
271 			size_t iLen, size_t *piLen) {
272 	unsigned short us, xid;
273 	static unsigned short xid_seeded = 0;
274 
275 	if (!xid_seeded) {
276 		static mutex_t lock = DEFAULTMUTEX;
277 		(void) mutex_lock(&lock);
278 		if (!xid_seeded) {
279 			/* generate a seed based on our PID */
280 			long long pid = getpid();
281 			pid *= UINT_MAX;
282 			(void) seed48((unsigned short *) &pid);
283 			xid_seeded = 1;
284 		}
285 		(void) mutex_unlock(&lock);
286 	}
287 	/* squish the random value into an unsigned short */
288 	xid = (unsigned short) (lrand48() % USHRT_MAX);
289 	xid = xid ? xid : 1;	/* 0 is for DAs only */
290 
291 	us = (unsigned short) strlen(pcLangTag);
292 	if ((SLP_HDRLEN + us) > iSendBufSz)
293 		return (SLP_PARAMETER_BAD);
294 
295 	(void) memset(pcSendBuf, 0, SLP_HDRLEN);
296 
297 	slp_set_version(pcSendBuf, SLP_VERSION);
298 	slp_set_function(pcSendBuf, (char)iFun);
299 	slp_set_length(pcSendBuf, iLen);
300 	slp_set_xid(pcSendBuf, xid);
301 	slp_set_langlen(pcSendBuf, us);
302 	(void) memcpy(&pcSendBuf[SLP_HDRLEN], pcLangTag, us);
303 
304 	*piLen = SLP_HDRLEN + us;
305 	return (SLP_OK);
306 }
307 
308 /*
309  * Retrieves the 24 bit int stored at 'off' offset into 'header'.
310  * Assumes 'header' is a valid SLP message header.
311  */
312 unsigned int slp_header_get_int24(const char *header, size_t off) {
313 	unsigned int len;
314 
315 	len = ((unsigned int)(header[off] & 0xff)) << 16;
316 	len += ((unsigned int)(header[off + 1] & 0xff)) << 8;
317 	len += ((unsigned int)(header[off + 2] & 0xff));
318 
319 	return (len);
320 }
321 
322 /*
323  * Sets a 24 bit int at the location in 'header' 'off' bytes
324  * offset into the header.
325  * Assumes 'header' is a valid SLP message header.
326  */
327 void slp_header_set_int24(char *header, unsigned int len, size_t off) {
328 	header[off] = (unsigned char) ((len & 0xff0000) >> 16);
329 	header[off + 1] = (unsigned char) ((len & 0xff00) >> 8);
330 	header[off + 2] = (unsigned char) (len & 0xff);
331 }
332 
333 /*
334  * Retrieves the 16 bit integer stored at 'off' offset into 'header'.
335  * Assumes 'header' is a valid SLP message header.
336  */
337 unsigned short slp_header_get_sht(const char *header, size_t off) {
338 	unsigned short answer = 0;
339 	(void) slp_get_sht(header, SLP_HDRLEN, &off, &answer);
340 	return (answer);
341 }
342 
343 /*
344  * Sets a 16 bit interger at the location in 'header' 'off' bytes
345  * offset into the header.
346  * Assumes 'header' is a valid SLP message header.
347  */
348 void slp_header_set_sht(char *header, unsigned short len, size_t off) {
349 	(void) slp_add_sht(header, SLP_HDRLEN, len, &off);
350 }
351 
352 /*
353  * Returns the total length of a SLP header associated with the SLP
354  * handle 'hp', including the language tag.
355  */
356 size_t slp_header_length(slp_handle_impl_t *hp) {
357 	return (SLP_HDRLEN + strlen(hp->locale));
358 }
359 
360 /*
361  * Retrieves the error code for UA replies -- the errcode is always
362  * the first short after the header for these functions. 'msg' points to
363  * the beginning of a SLP header.
364  */
365 slp_proto_err slp_get_errcode(char *msg) {
366 	unsigned short langlen, errcode;
367 	size_t off, msglen;
368 
369 	/* make sure the reply is long enough */
370 	msglen = slp_get_length(msg);
371 	if (msglen < (SLP_LANGLEN + 2))
372 		return (SLP_MSG_PARSE_ERROR);
373 	langlen = slp_get_langlen(msg);
374 	off = SLP_HDRLEN + langlen;
375 
376 	if (slp_get_sht(msg, msglen, &off, &errcode) != SLP_OK)
377 		return (SLP_MSG_PARSE_ERROR);
378 
379 	return (errcode);
380 }
381 
382 /*
383  * Primitive Encoding and Decoding Routines.
384  * All perform byte-ordering coversions and bounds checking.
385  */
386 
387 SLPError slp_add_byte(char *pcBuf, size_t iBufSz, int iVal,
388 			size_t *piLen) {
389 	if ((*piLen + 1) > iBufSz)
390 		return (SLP_PARAMETER_BAD);
391 
392 	pcBuf[(*piLen)++] = (unsigned char) iVal;
393 	return (SLP_OK);
394 }
395 
396 SLPError slp_add_sht(char *pcBuf, size_t iBufSz, unsigned short iVal,
397 			size_t *piLen) {
398 	if ((*piLen + 2) > iBufSz)
399 		return (SLP_PARAMETER_BAD);
400 
401 	pcBuf[(*piLen)++] = (unsigned char) ((iVal & 0xFF00) >> 8);
402 	pcBuf[(*piLen)++] = (unsigned char) (iVal & 0xFF);
403 	return (SLP_OK);
404 }
405 
406 SLPError slp_add_int32(char *pcBuf, size_t iBufSz, unsigned int iVal,
407 			size_t *piLen) {
408 	if ((*piLen + 4) > iBufSz)
409 		return (SLP_PARAMETER_BAD);
410 
411 	pcBuf[(*piLen)++] = (unsigned char) ((iVal & 0xFF000000) >> 24);
412 	pcBuf[(*piLen)++] = (unsigned char) ((iVal & 0xFF0000) >> 16);
413 	pcBuf[(*piLen)++] = (unsigned char) ((iVal & 0xFF00) >> 8);
414 	pcBuf[(*piLen)++] = (unsigned char) (iVal & 0xFF);
415 
416 	return (SLP_OK);
417 }
418 
419 SLPError slp_add_string(char *pcBuf, size_t iBufSz, const char *pcStr,
420 			size_t *piLen) {
421 	size_t iStrLen = strlen(pcStr);
422 	SLPError err = 0;
423 
424 	if (iStrLen > USHRT_MAX)
425 		/* SLP strings are limited to 16-bit len */
426 		return (SLP_PARAMETER_BAD);
427 	if ((iStrLen + *piLen + 2) > iBufSz)
428 		return (SLP_PARAMETER_BAD);
429 
430 	if ((err = slp_add_sht(pcBuf, iBufSz, (unsigned short)iStrLen, piLen))
431 	    != SLP_OK)
432 		return (err);
433 
434 	(void) memcpy(&(pcBuf[*piLen]), pcStr, iStrLen);
435 	*piLen += iStrLen;
436 	return (SLP_OK);
437 }
438 
439 SLPError slp_get_byte(const char *pcBuf, size_t maxlen,
440 			size_t *piOffset, int *piByte) {
441 	size_t offset = 0;
442 
443 	if (piOffset != NULL) {
444 		if ((*piOffset+1) > maxlen)
445 			return (SLP_PARSE_ERROR);
446 		offset = *piOffset;
447 		*piOffset += 1;
448 	}
449 
450 	*piByte = (int)pcBuf[offset];
451 	return (SLP_OK);
452 }
453 
454 SLPError slp_get_sht(const char *pcBuf, size_t maxlen,
455 			size_t *piOffset, unsigned short *piSht) {
456 	size_t offset = 0;
457 
458 	if (piOffset != NULL) {
459 		if ((*piOffset+2) > maxlen)
460 			return (SLP_PARSE_ERROR);
461 		offset = *piOffset;
462 		*piOffset += 2;
463 	}
464 
465 	*piSht = (unsigned short)
466 		((unsigned char)pcBuf[offset] & (unsigned char)0xFF);
467 	*piSht <<= 8;
468 	*piSht += (unsigned short)
469 		((unsigned char)pcBuf[offset+1] & (unsigned char)0xFF);
470 
471 	return (SLP_OK);
472 }
473 
474 SLPError slp_get_int32(const char *pcBuf, size_t maxlen,
475 			size_t *piOffset, unsigned int *piInt) {
476 	size_t offset = 0;
477 
478 	if (piOffset != NULL) {
479 		if ((*piOffset+4) > maxlen)
480 			return (SLP_PARSE_ERROR);
481 		offset = *piOffset;
482 		*piOffset += 4;
483 	}
484 
485 	*piInt = ((unsigned int)(pcBuf[offset] & 0xff)) << 24;
486 	*piInt += ((unsigned int)(pcBuf[offset+1] & 0xff)) << 16;
487 	*piInt += ((unsigned int)(pcBuf[offset+2] & 0xff)) << 8;
488 	*piInt += ((unsigned int)(pcBuf[offset+3] & 0xff));
489 
490 	return (SLP_OK);
491 }
492 
493 SLPError slp_get_string(const char *pcBuf, size_t iMaxLen,
494 		    size_t *piOffset, char **ppcString) {
495 	SLPError err;
496 	unsigned short iLen;
497 
498 	*ppcString = NULL;
499 	err = slp_get_sht(pcBuf, iMaxLen, piOffset, &iLen);
500 	if (err)
501 		return (err);
502 	if ((iLen+*piOffset) > iMaxLen)
503 		return (SLP_PARSE_ERROR);
504 
505 	if (!(*ppcString = malloc(iLen + 1))) {
506 		slp_err(LOG_CRIT, 0, "slp_get_string", "out of memory");
507 		return (SLP_MEMORY_ALLOC_FAILED);
508 	}
509 	(void) memcpy(*ppcString, pcBuf + *piOffset, iLen);
510 	(*ppcString)[iLen] = 0;
511 	*piOffset += iLen;
512 	return (SLP_OK);
513 }
514