1/*
2 * xdr.c, Generic XDR routines implementation.
3 *
4 * Copyright (c) 2010, Oracle America, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
16 * * Neither the name of the "Oracle America, Inc." nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * These are the "generic" xdr routines used to serialize and de-serialize
34 * most common data items. See xdr.h for more info on the interface to
35 * xdr.
36 */
37
38#include <stdio.h>
39#include <limits.h>
40#include <string.h>
41#include <libintl.h>
42#include <wchar.h>
43#include <stdint.h>
44
45#include <rpc/types.h>
46#include <rpc/xdr.h>
47#include <shlib-compat.h>
48
49
50/*
51 * constants specific to the xdr "protocol"
52 */
53#define XDR_FALSE ((long) 0)
54#define XDR_TRUE ((long) 1)
55#define LASTUNSIGNED ((u_int) 0-1)
56
57/*
58 * for unit alignment
59 */
60static const char xdr_zero[BYTES_PER_XDR_UNIT] = {0, 0, 0, 0};
61
62/*
63 * Free a data structure using XDR
64 * Not a filter, but a convenient utility nonetheless
65 */
66void
67xdr_free (xdrproc_t proc, char *objp)
68{
69 XDR x;
70
71 x.x_op = XDR_FREE;
72 (*proc) (&x, objp);
73}
74#ifdef EXPORT_RPC_SYMBOLS
75libc_hidden_def (xdr_free)
76#else
77libc_hidden_nolink_sunrpc (xdr_free, GLIBC_2_0)
78#endif
79
80/*
81 * XDR nothing
82 */
83bool_t
84xdr_void (void)
85{
86 return TRUE;
87}
88#ifdef EXPORT_RPC_SYMBOLS
89libc_hidden_def (xdr_void)
90#else
91libc_hidden_nolink_sunrpc (xdr_void, GLIBC_2_0)
92#endif
93
94/*
95 * XDR integers
96 */
97bool_t
98xdr_int (XDR *xdrs, int *ip)
99{
100
101#if INT_MAX < LONG_MAX
102 long l;
103
104 switch (xdrs->x_op)
105 {
106 case XDR_ENCODE:
107 l = (long) *ip;
108 return XDR_PUTLONG (xdrs, &l);
109
110 case XDR_DECODE:
111 if (!XDR_GETLONG (xdrs, &l))
112 {
113 return FALSE;
114 }
115 *ip = (int) l;
116 /* Fall through. */
117 case XDR_FREE:
118 return TRUE;
119 }
120 return FALSE;
121#elif INT_MAX == LONG_MAX
122 return xdr_long (xdrs, (long *) ip);
123#elif INT_MAX == SHRT_MAX
124 return xdr_short (xdrs, (short *) ip);
125#else
126#error unexpected integer sizes in_xdr_int()
127#endif
128}
129#ifdef EXPORT_RPC_SYMBOLS
130libc_hidden_def (xdr_int)
131#else
132libc_hidden_nolink_sunrpc (xdr_int, GLIBC_2_0)
133#endif
134
135/*
136 * XDR unsigned integers
137 */
138bool_t
139xdr_u_int (XDR *xdrs, u_int *up)
140{
141#if UINT_MAX < ULONG_MAX
142 long l;
143
144 switch (xdrs->x_op)
145 {
146 case XDR_ENCODE:
147 l = (u_long) * up;
148 return XDR_PUTLONG (xdrs, &l);
149
150 case XDR_DECODE:
151 if (!XDR_GETLONG (xdrs, &l))
152 {
153 return FALSE;
154 }
155 *up = (u_int) (u_long) l;
156 /* Fall through. */
157 case XDR_FREE:
158 return TRUE;
159 }
160 return FALSE;
161#elif UINT_MAX == ULONG_MAX
162 return xdr_u_long (xdrs, (u_long *) up);
163#elif UINT_MAX == USHRT_MAX
164 return xdr_short (xdrs, (short *) up);
165#else
166#error unexpected integer sizes in_xdr_u_int()
167#endif
168}
169#ifdef EXPORT_RPC_SYMBOLS
170libc_hidden_def (xdr_u_int)
171#else
172libc_hidden_nolink_sunrpc (xdr_u_int, GLIBC_2_0)
173#endif
174
175/*
176 * XDR long integers
177 * The definition of xdr_long() is kept for backward
178 * compatibility. Instead xdr_int() should be used.
179 */
180bool_t
181xdr_long (XDR *xdrs, long *lp)
182{
183
184 if (xdrs->x_op == XDR_ENCODE
185 && (sizeof (int32_t) == sizeof (long)
186 || (int32_t) *lp == *lp))
187 return XDR_PUTLONG (xdrs, lp);
188
189 if (xdrs->x_op == XDR_DECODE)
190 return XDR_GETLONG (xdrs, lp);
191
192 if (xdrs->x_op == XDR_FREE)
193 return TRUE;
194
195 return FALSE;
196}
197#ifdef EXPORT_RPC_SYMBOLS
198libc_hidden_def (xdr_long)
199#else
200libc_hidden_nolink_sunrpc (xdr_long, GLIBC_2_0)
201#endif
202
203/*
204 * XDR unsigned long integers
205 * The definition of xdr_u_long() is kept for backward
206 * compatibility. Instead xdr_u_int() should be used.
207 */
208bool_t
209xdr_u_long (XDR *xdrs, u_long *ulp)
210{
211 switch (xdrs->x_op)
212 {
213 case XDR_DECODE:
214 {
215 long int tmp;
216
217 if (XDR_GETLONG (xdrs, &tmp) == FALSE)
218 return FALSE;
219
220 *ulp = (uint32_t) tmp;
221 return TRUE;
222 }
223
224 case XDR_ENCODE:
225 if (sizeof (uint32_t) != sizeof (u_long)
226 && (uint32_t) *ulp != *ulp)
227 return FALSE;
228
229 return XDR_PUTLONG (xdrs, (long *) ulp);
230
231 case XDR_FREE:
232 return TRUE;
233 }
234 return FALSE;
235}
236#ifdef EXPORT_RPC_SYMBOLS
237libc_hidden_def (xdr_u_long)
238#else
239libc_hidden_nolink_sunrpc (xdr_u_long, GLIBC_2_0)
240#endif
241
242/*
243 * XDR hyper integers
244 * same as xdr_u_hyper - open coded to save a proc call!
245 */
246bool_t
247xdr_hyper (XDR *xdrs, quad_t *llp)
248{
249 long int t1, t2;
250
251 if (xdrs->x_op == XDR_ENCODE)
252 {
253 t1 = (long) ((*llp) >> 32);
254 t2 = (long) (*llp);
255 return (XDR_PUTLONG(xdrs, &t1) && XDR_PUTLONG(xdrs, &t2));
256 }
257
258 if (xdrs->x_op == XDR_DECODE)
259 {
260 if (!XDR_GETLONG(xdrs, &t1) || !XDR_GETLONG(xdrs, &t2))
261 return FALSE;
262 *llp = ((quad_t) t1) << 32;
263 *llp |= (uint32_t) t2;
264 return TRUE;
265 }
266
267 if (xdrs->x_op == XDR_FREE)
268 return TRUE;
269
270 return FALSE;
271}
272#ifdef EXPORT_RPC_SYMBOLS
273libc_hidden_def (xdr_hyper)
274#else
275libc_hidden_nolink_sunrpc (xdr_hyper, GLIBC_2_1_1)
276#endif
277
278/*
279 * XDR hyper integers
280 * same as xdr_hyper - open coded to save a proc call!
281 */
282bool_t
283xdr_u_hyper (XDR *xdrs, u_quad_t *ullp)
284{
285 long int t1, t2;
286
287 if (xdrs->x_op == XDR_ENCODE)
288 {
289 t1 = (unsigned long) ((*ullp) >> 32);
290 t2 = (unsigned long) (*ullp);
291 return (XDR_PUTLONG(xdrs, &t1) && XDR_PUTLONG(xdrs, &t2));
292 }
293
294 if (xdrs->x_op == XDR_DECODE)
295 {
296 if (!XDR_GETLONG(xdrs, &t1) || !XDR_GETLONG(xdrs, &t2))
297 return FALSE;
298 *ullp = ((u_quad_t) t1) << 32;
299 *ullp |= (uint32_t) t2;
300 return TRUE;
301 }
302
303 if (xdrs->x_op == XDR_FREE)
304 return TRUE;
305
306 return FALSE;
307}
308#ifdef EXPORT_RPC_SYMBOLS
309libc_hidden_def (xdr_u_hyper)
310#else
311libc_hidden_nolink_sunrpc (xdr_u_hyper, GLIBC_2_1_1)
312#endif
313
314bool_t
315xdr_longlong_t (XDR *xdrs, quad_t *llp)
316{
317 return xdr_hyper (xdrs, llp);
318}
319#ifdef EXPORT_RPC_SYMBOLS
320libc_hidden_def (xdr_longlong_t)
321#else
322libc_hidden_nolink_sunrpc (xdr_longlong_t, GLIBC_2_1_1)
323#endif
324
325bool_t
326xdr_u_longlong_t (XDR *xdrs, u_quad_t *ullp)
327{
328 return xdr_u_hyper (xdrs, ullp);
329}
330#ifdef EXPORT_RPC_SYMBOLS
331libc_hidden_def (xdr_u_longlong_t)
332#else
333libc_hidden_nolink_sunrpc (xdr_u_longlong_t, GLIBC_2_1_1)
334#endif
335
336/*
337 * XDR short integers
338 */
339bool_t
340xdr_short (XDR *xdrs, short *sp)
341{
342 long l;
343
344 switch (xdrs->x_op)
345 {
346 case XDR_ENCODE:
347 l = (long) *sp;
348 return XDR_PUTLONG (xdrs, &l);
349
350 case XDR_DECODE:
351 if (!XDR_GETLONG (xdrs, &l))
352 {
353 return FALSE;
354 }
355 *sp = (short) l;
356 return TRUE;
357
358 case XDR_FREE:
359 return TRUE;
360 }
361 return FALSE;
362}
363#ifdef EXPORT_RPC_SYMBOLS
364libc_hidden_def (xdr_short)
365#else
366libc_hidden_nolink_sunrpc (xdr_short, GLIBC_2_0)
367#endif
368
369/*
370 * XDR unsigned short integers
371 */
372bool_t
373xdr_u_short (XDR *xdrs, u_short *usp)
374{
375 long l;
376
377 switch (xdrs->x_op)
378 {
379 case XDR_ENCODE:
380 l = (u_long) * usp;
381 return XDR_PUTLONG (xdrs, &l);
382
383 case XDR_DECODE:
384 if (!XDR_GETLONG (xdrs, &l))
385 {
386 return FALSE;
387 }
388 *usp = (u_short) (u_long) l;
389 return TRUE;
390
391 case XDR_FREE:
392 return TRUE;
393 }
394 return FALSE;
395}
396#ifdef EXPORT_RPC_SYMBOLS
397libc_hidden_def (xdr_u_short)
398#else
399libc_hidden_nolink_sunrpc (xdr_u_short, GLIBC_2_0)
400#endif
401
402
403/*
404 * XDR a char
405 */
406bool_t
407xdr_char (XDR *xdrs, char *cp)
408{
409 int i;
410
411 i = (*cp);
412 if (!xdr_int (xdrs, ip: &i))
413 {
414 return FALSE;
415 }
416 *cp = i;
417 return TRUE;
418}
419#ifdef EXPORT_RPC_SYMBOLS
420libc_hidden_def (xdr_char)
421#else
422libc_hidden_nolink_sunrpc (xdr_char, GLIBC_2_0)
423#endif
424
425/*
426 * XDR an unsigned char
427 */
428bool_t
429xdr_u_char (XDR *xdrs, u_char *cp)
430{
431 u_int u;
432
433 u = (*cp);
434 if (!xdr_u_int (xdrs, up: &u))
435 {
436 return FALSE;
437 }
438 *cp = u;
439 return TRUE;
440}
441#ifdef EXPORT_RPC_SYMBOLS
442libc_hidden_def (xdr_u_char)
443#else
444libc_hidden_nolink_sunrpc (xdr_u_char, GLIBC_2_0)
445#endif
446
447/*
448 * XDR booleans
449 */
450bool_t
451xdr_bool (XDR *xdrs, bool_t *bp)
452{
453 long lb;
454
455 switch (xdrs->x_op)
456 {
457 case XDR_ENCODE:
458 lb = *bp ? XDR_TRUE : XDR_FALSE;
459 return XDR_PUTLONG (xdrs, &lb);
460
461 case XDR_DECODE:
462 if (!XDR_GETLONG (xdrs, &lb))
463 {
464 return FALSE;
465 }
466 *bp = (lb == XDR_FALSE) ? FALSE : TRUE;
467 return TRUE;
468
469 case XDR_FREE:
470 return TRUE;
471 }
472 return FALSE;
473}
474#ifdef EXPORT_RPC_SYMBOLS
475libc_hidden_def (xdr_bool)
476#else
477libc_hidden_nolink_sunrpc (xdr_bool, GLIBC_2_0)
478#endif
479
480/*
481 * XDR enumerations
482 */
483bool_t
484xdr_enum (XDR *xdrs, enum_t *ep)
485{
486 enum sizecheck
487 {
488 SIZEVAL
489 }; /* used to find the size of an enum */
490
491 /*
492 * enums are treated as ints
493 */
494 if (sizeof (enum sizecheck) == 4)
495 {
496#if INT_MAX < LONG_MAX
497 long l;
498
499 switch (xdrs->x_op)
500 {
501 case XDR_ENCODE:
502 l = *ep;
503 return XDR_PUTLONG (xdrs, &l);
504
505 case XDR_DECODE:
506 if (!XDR_GETLONG (xdrs, &l))
507 {
508 return FALSE;
509 }
510 *ep = l;
511 /* Fall through. */
512 case XDR_FREE:
513 return TRUE;
514
515 }
516 return FALSE;
517#else
518 return xdr_long (xdrs, (long *) ep);
519#endif
520 }
521 else if (sizeof (enum sizecheck) == sizeof (short))
522 {
523 return xdr_short (xdrs, sp: (short *) ep);
524 }
525 else
526 {
527 return FALSE;
528 }
529}
530#ifdef EXPORT_RPC_SYMBOLS
531libc_hidden_def (xdr_enum)
532#else
533libc_hidden_nolink_sunrpc (xdr_enum, GLIBC_2_0)
534#endif
535
536/*
537 * XDR opaque data
538 * Allows the specification of a fixed size sequence of opaque bytes.
539 * cp points to the opaque object and cnt gives the byte length.
540 */
541bool_t
542xdr_opaque (XDR *xdrs, caddr_t cp, u_int cnt)
543{
544 u_int rndup;
545 static char crud[BYTES_PER_XDR_UNIT];
546
547 /*
548 * if no data we are done
549 */
550 if (cnt == 0)
551 return TRUE;
552
553 /*
554 * round byte count to full xdr units
555 */
556 rndup = cnt % BYTES_PER_XDR_UNIT;
557 if (rndup > 0)
558 rndup = BYTES_PER_XDR_UNIT - rndup;
559
560 switch (xdrs->x_op)
561 {
562 case XDR_DECODE:
563 if (!XDR_GETBYTES (xdrs, cp, cnt))
564 {
565 return FALSE;
566 }
567 if (rndup == 0)
568 return TRUE;
569 return XDR_GETBYTES (xdrs, (caddr_t)crud, rndup);
570
571 case XDR_ENCODE:
572 if (!XDR_PUTBYTES (xdrs, cp, cnt))
573 {
574 return FALSE;
575 }
576 if (rndup == 0)
577 return TRUE;
578 return XDR_PUTBYTES (xdrs, xdr_zero, rndup);
579
580 case XDR_FREE:
581 return TRUE;
582 }
583 return FALSE;
584}
585#ifdef EXPORT_RPC_SYMBOLS
586libc_hidden_def (xdr_opaque)
587#else
588libc_hidden_nolink_sunrpc (xdr_opaque, GLIBC_2_0)
589#endif
590
591/*
592 * XDR counted bytes
593 * *cpp is a pointer to the bytes, *sizep is the count.
594 * If *cpp is NULL maxsize bytes are allocated
595 */
596bool_t
597xdr_bytes (XDR *xdrs, char **cpp, u_int *sizep, u_int maxsize)
598{
599 char *sp = *cpp; /* sp is the actual string pointer */
600 u_int nodesize;
601
602 /*
603 * first deal with the length since xdr bytes are counted
604 */
605 if (!xdr_u_int (xdrs, up: sizep))
606 {
607 return FALSE;
608 }
609 nodesize = *sizep;
610 if ((nodesize > maxsize) && (xdrs->x_op != XDR_FREE))
611 {
612 return FALSE;
613 }
614
615 /*
616 * now deal with the actual bytes
617 */
618 switch (xdrs->x_op)
619 {
620 case XDR_DECODE:
621 if (nodesize == 0)
622 {
623 return TRUE;
624 }
625 if (sp == NULL)
626 {
627 *cpp = sp = (char *) mem_alloc (nodesize);
628 }
629 if (sp == NULL)
630 {
631 (void) __fxprintf (NULL, fmt: "%s: %s", __func__, _("out of memory\n"));
632 return FALSE;
633 }
634 /* Fall through. */
635
636 case XDR_ENCODE:
637 return xdr_opaque (xdrs, cp: sp, cnt: nodesize);
638
639 case XDR_FREE:
640 if (sp != NULL)
641 {
642 mem_free (sp, nodesize);
643 *cpp = NULL;
644 }
645 return TRUE;
646 }
647 return FALSE;
648}
649#ifdef EXPORT_RPC_SYMBOLS
650libc_hidden_def (xdr_bytes)
651#else
652libc_hidden_nolink_sunrpc (xdr_bytes, GLIBC_2_0)
653#endif
654
655/*
656 * Implemented here due to commonality of the object.
657 */
658bool_t
659xdr_netobj (XDR *xdrs, struct netobj *np)
660{
661
662 return xdr_bytes (xdrs, cpp: &np->n_bytes, sizep: &np->n_len, MAX_NETOBJ_SZ);
663}
664#ifdef EXPORT_RPC_SYMBOLS
665libc_hidden_def (xdr_netobj)
666#else
667libc_hidden_nolink_sunrpc (xdr_netobj, GLIBC_2_0)
668#endif
669
670/*
671 * XDR a discriminated union
672 * Support routine for discriminated unions.
673 * You create an array of xdrdiscrim structures, terminated with
674 * an entry with a null procedure pointer. The routine gets
675 * the discriminant value and then searches the array of xdrdiscrims
676 * looking for that value. It calls the procedure given in the xdrdiscrim
677 * to handle the discriminant. If there is no specific routine a default
678 * routine may be called.
679 * If there is no specific or default routine an error is returned.
680 */
681bool_t
682xdr_union (XDR *xdrs,
683 /* enum to decide which arm to work on */
684 enum_t *dscmp,
685 /* the union itself */
686 char *unp,
687 /* [value, xdr proc] for each arm */
688 const struct xdr_discrim *choices,
689 /* default xdr routine */
690 xdrproc_t dfault)
691{
692 enum_t dscm;
693
694 /*
695 * we deal with the discriminator; it's an enum
696 */
697 if (!xdr_enum (xdrs, ep: dscmp))
698 {
699 return FALSE;
700 }
701 dscm = *dscmp;
702
703 /*
704 * search choices for a value that matches the discriminator.
705 * if we find one, execute the xdr routine for that value.
706 */
707 for (; choices->proc != NULL_xdrproc_t; choices++)
708 {
709 if (choices->value == dscm)
710 return (*(choices->proc)) (xdrs, unp, LASTUNSIGNED);
711 }
712
713 /*
714 * no match - execute the default xdr routine if there is one
715 */
716 return ((dfault == NULL_xdrproc_t) ? FALSE :
717 (*dfault) (xdrs, unp, LASTUNSIGNED));
718}
719libc_hidden_nolink_sunrpc (xdr_union, GLIBC_2_0)
720
721
722/*
723 * Non-portable xdr primitives.
724 * Care should be taken when moving these routines to new architectures.
725 */
726
727
728/*
729 * XDR null terminated ASCII strings
730 * xdr_string deals with "C strings" - arrays of bytes that are
731 * terminated by a NUL character. The parameter cpp references a
732 * pointer to storage; If the pointer is null, then the necessary
733 * storage is allocated. The last parameter is the max allowed length
734 * of the string as specified by a protocol.
735 */
736bool_t
737xdr_string (XDR *xdrs, char **cpp, u_int maxsize)
738{
739 char *sp = *cpp; /* sp is the actual string pointer */
740 /* Initialize to silence the compiler. It is not really needed because SIZE
741 never actually gets used without being initialized. */
742 u_int size = 0;
743 u_int nodesize;
744
745 /*
746 * first deal with the length since xdr strings are counted-strings
747 */
748 switch (xdrs->x_op)
749 {
750 case XDR_FREE:
751 if (sp == NULL)
752 {
753 return TRUE; /* already free */
754 }
755 /* fall through... */
756 case XDR_ENCODE:
757 if (sp == NULL)
758 return FALSE;
759 size = strlen (sp);
760 break;
761 case XDR_DECODE:
762 break;
763 }
764 if (!xdr_u_int (xdrs, up: &size))
765 {
766 return FALSE;
767 }
768 if (size > maxsize)
769 {
770 return FALSE;
771 }
772 nodesize = size + 1;
773 if (nodesize == 0)
774 {
775 /* This means an overflow. It a bug in the caller which
776 provided a too large maxsize but nevertheless catch it
777 here. */
778 return FALSE;
779 }
780
781 /*
782 * now deal with the actual bytes
783 */
784 switch (xdrs->x_op)
785 {
786 case XDR_DECODE:
787 if (sp == NULL)
788 *cpp = sp = (char *) mem_alloc (nodesize);
789 if (sp == NULL)
790 {
791 (void) __fxprintf (NULL, fmt: "%s: %s", __func__, _("out of memory\n"));
792 return FALSE;
793 }
794 sp[size] = 0;
795 /* Fall through. */
796
797 case XDR_ENCODE:
798 return xdr_opaque (xdrs, cp: sp, cnt: size);
799
800 case XDR_FREE:
801 mem_free (sp, nodesize);
802 *cpp = NULL;
803 return TRUE;
804 }
805 return FALSE;
806}
807#ifdef EXPORT_RPC_SYMBOLS
808libc_hidden_def (xdr_string)
809#else
810libc_hidden_nolink_sunrpc (xdr_string, GLIBC_2_0)
811#endif
812
813/*
814 * Wrapper for xdr_string that can be called directly from
815 * routines like clnt_call
816 */
817bool_t
818xdr_wrapstring (XDR *xdrs, char **cpp)
819{
820 if (xdr_string (xdrs, cpp, LASTUNSIGNED))
821 {
822 return TRUE;
823 }
824 return FALSE;
825}
826#ifdef EXPORT_RPC_SYMBOLS
827libc_hidden_def (xdr_wrapstring)
828#else
829libc_hidden_nolink_sunrpc (xdr_wrapstring, GLIBC_2_0)
830#endif
831

source code of glibc/sunrpc/xdr.c