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