xref: /illumos-gate/usr/src/uts/common/rpc/xdr.c (revision 7c478bd9)
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 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * Portions of this source code were derived from Berkeley 4.3 BSD
32  * under license from the Regents of the University of California.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 /*
38  * xdr.c, generic XDR routines implementation.
39  * These are the "generic" xdr routines used to serialize and de-serialize
40  * most common data items.  See xdr.h for more info on the interface to
41  * xdr.
42  */
43 
44 #include <sys/param.h>
45 #include <sys/cmn_err.h>
46 #include <sys/types.h>
47 #include <sys/systm.h>
48 
49 #include <rpc/types.h>
50 #include <rpc/xdr.h>
51 #include <sys/isa_defs.h>
52 
53 #pragma weak xdr_int32_t = xdr_int
54 #pragma weak xdr_uint32_t = xdr_u_int
55 #pragma weak xdr_int64_t = xdr_longlong_t
56 #pragma weak xdr_uint64_t = xdr_u_longlong_t
57 
58 #if !defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
59 #error "Exactly one of _BIG_ENDIAN or _LITTLE_ENDIAN must be defined"
60 #elif defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN)
61 #error "Only one of _BIG_ENDIAN or _LITTLE_ENDIAN may be defined"
62 #endif
63 
64 /*
65  * constants specific to the xdr "protocol"
66  */
67 #define	XDR_FALSE	((int32_t)0)
68 #define	XDR_TRUE	((int32_t)1)
69 #define	LASTUNSIGNED	((uint_t)0-1)
70 
71 /*
72  * for unit alignment
73  */
74 static char xdr_zero[BYTES_PER_XDR_UNIT] = { 0, 0, 0, 0 };
75 
76 /*
77  * Free a data structure using XDR
78  * Not a filter, but a convenient utility nonetheless
79  */
80 void
81 xdr_free(xdrproc_t proc, char *objp)
82 {
83 	XDR x;
84 
85 	x.x_op = XDR_FREE;
86 	(*proc)(&x, objp);
87 }
88 
89 /*
90  * XDR nothing
91  */
92 bool_t
93 xdr_void(void)
94 {
95 	return (TRUE);
96 }
97 
98 /*
99  * XDR integers
100  *
101  * PSARC 2003/523 Contract Private Interface
102  * xdr_int
103  * Changes must be reviewed by Solaris File Sharing
104  * Changes must be communicated to contract-2003-523@sun.com
105  */
106 bool_t
107 xdr_int(XDR *xdrs, int *ip)
108 {
109 	if (xdrs->x_op == XDR_ENCODE)
110 		return (XDR_PUTINT32(xdrs, ip));
111 
112 	if (xdrs->x_op == XDR_DECODE)
113 		return (XDR_GETINT32(xdrs, ip));
114 
115 	if (xdrs->x_op == XDR_FREE)
116 		return (TRUE);
117 
118 #ifdef DEBUG
119 	printf("xdr_int: FAILED\n");
120 #endif
121 	return (FALSE);
122 }
123 
124 /*
125  * XDR unsigned integers
126  *
127  * PSARC 2003/523 Contract Private Interface
128  * xdr_u_int
129  * Changes must be reviewed by Solaris File Sharing
130  * Changes must be communicated to contract-2003-523@sun.com
131  */
132 bool_t
133 xdr_u_int(XDR *xdrs, uint_t *up)
134 {
135 	if (xdrs->x_op == XDR_ENCODE)
136 		return (XDR_PUTINT32(xdrs, (int32_t *)up));
137 
138 	if (xdrs->x_op == XDR_DECODE)
139 		return (XDR_GETINT32(xdrs, (int32_t *)up));
140 
141 	if (xdrs->x_op == XDR_FREE)
142 		return (TRUE);
143 
144 #ifdef DEBUG
145 	printf("xdr_int: FAILED\n");
146 #endif
147 	return (FALSE);
148 }
149 
150 
151 #if defined(_ILP32)
152 /*
153  * xdr_long and xdr_u_long for binary compatability on ILP32 kernels.
154  *
155  * No prototypes since new code should not be using these interfaces.
156  */
157 bool_t
158 xdr_long(XDR *xdrs, long *ip)
159 {
160 	return (xdr_int(xdrs, (int *)ip));
161 }
162 
163 bool_t
164 xdr_u_long(XDR *xdrs, unsigned long *up)
165 {
166 	return (xdr_u_int(xdrs, (uint_t *)up));
167 }
168 #endif /* _ILP32 */
169 
170 
171 /*
172  * XDR long long integers
173  */
174 bool_t
175 xdr_longlong_t(XDR *xdrs, longlong_t *hp)
176 {
177 	if (xdrs->x_op == XDR_ENCODE) {
178 #if defined(_LITTLE_ENDIAN)
179 		if (XDR_PUTINT32(xdrs, (int32_t *)((char *)hp +
180 		    BYTES_PER_XDR_UNIT)) == TRUE) {
181 			return (XDR_PUTINT32(xdrs, (int32_t *)hp));
182 		}
183 #elif defined(_BIG_ENDIAN)
184 		if (XDR_PUTINT32(xdrs, (int32_t *)hp) == TRUE) {
185 			return (XDR_PUTINT32(xdrs, (int32_t *)((char *)hp +
186 			    BYTES_PER_XDR_UNIT)));
187 		}
188 #endif
189 		return (FALSE);
190 
191 	}
192 	if (xdrs->x_op == XDR_DECODE) {
193 #if defined(_LITTLE_ENDIAN)
194 		if (XDR_GETINT32(xdrs, (int32_t *)((char *)hp +
195 		    BYTES_PER_XDR_UNIT)) == TRUE) {
196 			return (XDR_GETINT32(xdrs, (int32_t *)hp));
197 		}
198 #elif defined(_BIG_ENDIAN)
199 		if (XDR_GETINT32(xdrs, (int32_t *)hp) == TRUE) {
200 			return (XDR_GETINT32(xdrs, (int32_t *)((char *)hp +
201 			    BYTES_PER_XDR_UNIT)));
202 		}
203 #endif
204 		return (FALSE);
205 	}
206 	return (TRUE);
207 }
208 
209 /*
210  * XDR unsigned long long integers
211  */
212 bool_t
213 xdr_u_longlong_t(XDR *xdrs, u_longlong_t *hp)
214 {
215 
216 	if (xdrs->x_op == XDR_ENCODE) {
217 #if defined(_LITTLE_ENDIAN)
218 		if (XDR_PUTINT32(xdrs, (int32_t *)((char *)hp +
219 		    BYTES_PER_XDR_UNIT)) == TRUE) {
220 			return (XDR_PUTINT32(xdrs, (int32_t *)hp));
221 		}
222 #elif defined(_BIG_ENDIAN)
223 		if (XDR_PUTINT32(xdrs, (int32_t *)hp) == TRUE) {
224 			return (XDR_PUTINT32(xdrs, (int32_t *)((char *)hp +
225 			    BYTES_PER_XDR_UNIT)));
226 		}
227 #endif
228 		return (FALSE);
229 
230 	}
231 	if (xdrs->x_op == XDR_DECODE) {
232 #if defined(_LITTLE_ENDIAN)
233 		if (XDR_GETINT32(xdrs, (int32_t *)((char *)hp +
234 		    BYTES_PER_XDR_UNIT)) == TRUE) {
235 			return (XDR_GETINT32(xdrs, (int32_t *)hp));
236 		}
237 #elif defined(_BIG_ENDIAN)
238 		if (XDR_GETINT32(xdrs, (int32_t *)hp) == TRUE) {
239 			return (XDR_GETINT32(xdrs, (int32_t *)((char *)hp +
240 			    BYTES_PER_XDR_UNIT)));
241 		}
242 #endif
243 		return (FALSE);
244 	}
245 	return (TRUE);
246 }
247 
248 /*
249  * XDR short integers
250  */
251 bool_t
252 xdr_short(XDR *xdrs, short *sp)
253 {
254 	int32_t l;
255 
256 	switch (xdrs->x_op) {
257 
258 	case XDR_ENCODE:
259 		l = (int32_t)*sp;
260 		return (XDR_PUTINT32(xdrs, &l));
261 
262 	case XDR_DECODE:
263 		if (!XDR_GETINT32(xdrs, &l))
264 			return (FALSE);
265 		*sp = (short)l;
266 		return (TRUE);
267 
268 	case XDR_FREE:
269 		return (TRUE);
270 	}
271 	return (FALSE);
272 }
273 
274 /*
275  * XDR unsigned short integers
276  */
277 bool_t
278 xdr_u_short(XDR *xdrs, ushort_t *usp)
279 {
280 	uint32_t l;
281 
282 	switch (xdrs->x_op) {
283 
284 	case XDR_ENCODE:
285 		l = (uint32_t)*usp;
286 		return (XDR_PUTINT32(xdrs, (int32_t *)&l));
287 
288 	case XDR_DECODE:
289 		if (!XDR_GETINT32(xdrs, (int32_t *)&l)) {
290 #ifdef DEBUG
291 			printf("xdr_u_short: decode FAILED\n");
292 #endif
293 			return (FALSE);
294 		}
295 		*usp = (ushort_t)l;
296 		return (TRUE);
297 
298 	case XDR_FREE:
299 		return (TRUE);
300 	}
301 #ifdef DEBUG
302 	printf("xdr_u_short: bad op FAILED\n");
303 #endif
304 	return (FALSE);
305 }
306 
307 
308 /*
309  * XDR a char
310  */
311 bool_t
312 xdr_char(XDR *xdrs, char *cp)
313 {
314 	int i;
315 
316 	i = (*cp);
317 	if (!xdr_int(xdrs, &i)) {
318 		return (FALSE);
319 	}
320 	*cp = (char)i;
321 	return (TRUE);
322 }
323 
324 /*
325  * XDR booleans
326  *
327  * PSARC 2003/523 Contract Private Interface
328  * xdr_bool
329  * Changes must be reviewed by Solaris File Sharing
330  * Changes must be communicated to contract-2003-523@sun.com
331  */
332 bool_t
333 xdr_bool(XDR *xdrs, bool_t *bp)
334 {
335 	int32_t i32b;
336 
337 	switch (xdrs->x_op) {
338 
339 	case XDR_ENCODE:
340 		i32b = *bp ? XDR_TRUE : XDR_FALSE;
341 		return (XDR_PUTINT32(xdrs, &i32b));
342 
343 	case XDR_DECODE:
344 		if (!XDR_GETINT32(xdrs, &i32b)) {
345 #ifdef DEBUG
346 			printf("xdr_bool: decode FAILED\n");
347 #endif
348 			return (FALSE);
349 		}
350 		*bp = (i32b == XDR_FALSE) ? FALSE : TRUE;
351 		return (TRUE);
352 
353 	case XDR_FREE:
354 		return (TRUE);
355 	}
356 #ifdef DEBUG
357 	printf("xdr_bool: bad op FAILED\n");
358 #endif
359 	return (FALSE);
360 }
361 
362 /*
363  * XDR enumerations
364  *
365  * PSARC 2003/523 Contract Private Interface
366  * xdr_enum
367  * Changes must be reviewed by Solaris File Sharing
368  * Changes must be communicated to contract-2003-523@sun.com
369  */
370 #ifndef lint
371 enum sizecheck { SIZEVAL } sizecheckvar;	/* used to find the size of */
372 						/* an enum */
373 #endif
374 bool_t
375 xdr_enum(XDR *xdrs, enum_t *ep)
376 {
377 #ifndef lint
378 	/*
379 	 * enums are treated as ints
380 	 */
381 	if (sizeof (sizecheckvar) == sizeof (int32_t)) {
382 		return (xdr_int(xdrs, (int32_t *)ep));
383 	} else if (sizeof (sizecheckvar) == sizeof (short)) {
384 		return (xdr_short(xdrs, (short *)ep));
385 	} else {
386 		return (FALSE);
387 	}
388 #else
389 	(void) (xdr_short(xdrs, (short *)ep));
390 	return (xdr_int(xdrs, (int32_t *)ep));
391 #endif
392 }
393 
394 /*
395  * XDR opaque data
396  * Allows the specification of a fixed size sequence of opaque bytes.
397  * cp points to the opaque object and cnt gives the byte length.
398  *
399  * PSARC 2003/523 Contract Private Interface
400  * xdr_opaque
401  * Changes must be reviewed by Solaris File Sharing
402  * Changes must be communicated to contract-2003-523@sun.com
403  */
404 bool_t
405 xdr_opaque(XDR *xdrs, caddr_t cp, const uint_t cnt)
406 {
407 	uint_t rndup;
408 	static char crud[BYTES_PER_XDR_UNIT];
409 
410 	/*
411 	 * if no data we are done
412 	 */
413 	if (cnt == 0)
414 		return (TRUE);
415 
416 	/*
417 	 * round byte count to full xdr units
418 	 */
419 	rndup = cnt % BYTES_PER_XDR_UNIT;
420 	if (rndup != 0)
421 		rndup = BYTES_PER_XDR_UNIT - rndup;
422 
423 	if (xdrs->x_op == XDR_DECODE) {
424 		if (!XDR_GETBYTES(xdrs, cp, cnt)) {
425 #ifdef DEBUG
426 			printf("xdr_opaque: decode FAILED\n");
427 #endif
428 			return (FALSE);
429 		}
430 		if (rndup == 0)
431 			return (TRUE);
432 		return (XDR_GETBYTES(xdrs, (caddr_t)crud, rndup));
433 	}
434 
435 	if (xdrs->x_op == XDR_ENCODE) {
436 		if (!XDR_PUTBYTES(xdrs, cp, cnt)) {
437 #ifdef DEBUG
438 			printf("xdr_opaque: encode FAILED\n");
439 #endif
440 			return (FALSE);
441 		}
442 		if (rndup == 0)
443 			return (TRUE);
444 		return (XDR_PUTBYTES(xdrs, xdr_zero, rndup));
445 	}
446 
447 	if (xdrs->x_op == XDR_FREE)
448 		return (TRUE);
449 
450 #ifdef DEBUG
451 	printf("xdr_opaque: bad op FAILED\n");
452 #endif
453 	return (FALSE);
454 }
455 
456 /*
457  * XDR counted bytes
458  * *cpp is a pointer to the bytes, *sizep is the count.
459  * If *cpp is NULL maxsize bytes are allocated
460  *
461  * PSARC 2003/523 Contract Private Interface
462  * xdr_bytes
463  * Changes must be reviewed by Solaris File Sharing
464  * Changes must be communicated to contract-2003-523@sun.com
465  */
466 bool_t
467 xdr_bytes(XDR *xdrs, char **cpp, uint_t *sizep, const uint_t maxsize)
468 {
469 	char *sp = *cpp;  /* sp is the actual string pointer */
470 	uint_t nodesize;
471 
472 	/*
473 	 * first deal with the length since xdr bytes are counted
474 	 */
475 	if (!xdr_u_int(xdrs, sizep)) {
476 #ifdef DEBUG
477 		printf("xdr_bytes: size FAILED\n");
478 #endif
479 		return (FALSE);
480 	}
481 	nodesize = *sizep;
482 	if ((nodesize > maxsize) && (xdrs->x_op != XDR_FREE)) {
483 #ifdef DEBUG
484 		printf("xdr_bytes: bad size (%d) FAILED (%d max)\n",
485 		    nodesize, maxsize);
486 #endif
487 		return (FALSE);
488 	}
489 
490 	/*
491 	 * now deal with the actual bytes
492 	 */
493 	switch (xdrs->x_op) {
494 	case XDR_DECODE:
495 		if (nodesize == 0)
496 			return (TRUE);
497 		if (sp == NULL)
498 			*cpp = sp = (char *)mem_alloc(nodesize);
499 		/* FALLTHROUGH */
500 
501 	case XDR_ENCODE:
502 		return (xdr_opaque(xdrs, sp, nodesize));
503 
504 	case XDR_FREE:
505 		if (sp != NULL) {
506 			mem_free(sp, nodesize);
507 			*cpp = NULL;
508 		}
509 		return (TRUE);
510 	}
511 #ifdef DEBUG
512 	printf("xdr_bytes: bad op FAILED\n");
513 #endif
514 	return (FALSE);
515 }
516 
517 /*
518  * Implemented here due to commonality of the object.
519  */
520 bool_t
521 xdr_netobj(XDR *xdrs, struct netobj *np)
522 {
523 	return (xdr_bytes(xdrs, &np->n_bytes, &np->n_len, MAX_NETOBJ_SZ));
524 }
525 
526 /*
527  * XDR a descriminated union
528  * Support routine for discriminated unions.
529  * You create an array of xdrdiscrim structures, terminated with
530  * an entry with a null procedure pointer.  The routine gets
531  * the discriminant value and then searches the array of xdrdiscrims
532  * looking for that value.  It calls the procedure given in the xdrdiscrim
533  * to handle the discriminant.  If there is no specific routine a default
534  * routine may be called.
535  * If there is no specific or default routine an error is returned.
536  */
537 bool_t
538 xdr_union(XDR *xdrs, enum_t *dscmp, char *unp,
539 	const struct xdr_discrim *choices, const xdrproc_t dfault)
540 {
541 	enum_t dscm;
542 
543 	/*
544 	 * we deal with the discriminator;  it's an enum
545 	 */
546 	if (!xdr_enum(xdrs, dscmp)) {
547 #ifdef DEBUG
548 		printf("xdr_enum: dscmp FAILED\n");
549 #endif
550 		return (FALSE);
551 	}
552 	dscm = *dscmp;
553 
554 	/*
555 	 * search choices for a value that matches the discriminator.
556 	 * if we find one, execute the xdr routine for that value.
557 	 */
558 	for (; choices->proc != NULL_xdrproc_t; choices++) {
559 		if (choices->value == dscm)
560 			return ((*(choices->proc))(xdrs, unp, LASTUNSIGNED));
561 	}
562 
563 	/*
564 	 * no match - execute the default xdr routine if there is one
565 	 */
566 	return ((dfault == NULL_xdrproc_t) ? FALSE :
567 	    (*dfault)(xdrs, unp, LASTUNSIGNED));
568 }
569 
570 
571 /*
572  * Non-portable xdr primitives.
573  * Care should be taken when moving these routines to new architectures.
574  */
575 
576 
577 /*
578  * XDR null terminated ASCII strings
579  * xdr_string deals with "C strings" - arrays of bytes that are
580  * terminated by a NULL character.  The parameter cpp references a
581  * pointer to storage; If the pointer is null, then the necessary
582  * storage is allocated.  The last parameter is the max allowed length
583  * of the string as specified by a protocol.
584  */
585 bool_t
586 xdr_string(XDR *xdrs, char **cpp, const uint_t maxsize)
587 {
588 	char *sp = *cpp;  /* sp is the actual string pointer */
589 	uint_t size;
590 	uint_t nodesize;
591 
592 	/*
593 	 * first deal with the length since xdr strings are counted-strings
594 	 */
595 	switch (xdrs->x_op) {
596 	case XDR_FREE:
597 		if (sp == NULL)
598 			return (TRUE);	/* already free */
599 		/* FALLTHROUGH */
600 	case XDR_ENCODE:
601 		size = (sp != NULL) ? (uint_t)strlen(sp) : 0;
602 		break;
603 	case XDR_DECODE:
604 		break;
605 	}
606 	if (!xdr_u_int(xdrs, &size)) {
607 #ifdef DEBUG
608 		printf("xdr_string: size FAILED\n");
609 #endif
610 		return (FALSE);
611 	}
612 	if (size > maxsize) {
613 #ifdef DEBUG
614 		printf("xdr_string: bad size FAILED\n");
615 #endif
616 		return (FALSE);
617 	}
618 	nodesize = size + 1;
619 
620 	/*
621 	 * now deal with the actual bytes
622 	 */
623 	switch (xdrs->x_op) {
624 	case XDR_DECODE:
625 		if (nodesize == 0)
626 			return (TRUE);
627 		if (sp == NULL)
628 			sp = (char *)mem_alloc(nodesize);
629 		sp[size] = 0;
630 		if (!xdr_opaque(xdrs, sp, size)) {
631 			/*
632 			 * free up memory if allocated here
633 			 */
634 			if (*cpp == NULL) {
635 				mem_free(sp, nodesize);
636 			}
637 			return (FALSE);
638 		}
639 		if (strlen(sp) != size) {
640 			if (*cpp == NULL) {
641 				mem_free(sp, nodesize);
642 			}
643 			return (FALSE);
644 		}
645 		*cpp = sp;
646 		return (TRUE);
647 
648 	case XDR_ENCODE:
649 		return (xdr_opaque(xdrs, sp, size));
650 
651 	case XDR_FREE:
652 		mem_free(sp, nodesize);
653 		*cpp = NULL;
654 		return (TRUE);
655 	}
656 #ifdef DEBUG
657 	printf("xdr_string: bad op FAILED\n");
658 #endif
659 	return (FALSE);
660 }
661 
662 /*
663  * Wrapper for xdr_string that can be called directly from
664  * routines like clnt_call
665  */
666 bool_t
667 xdr_wrapstring(XDR *xdrs, char **cpp)
668 {
669 	if (xdr_string(xdrs, cpp, LASTUNSIGNED))
670 		return (TRUE);
671 	return (FALSE);
672 }
673