home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume6 / rpc2 / part07 / rpc / rpclib / rpc_prot.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-11-30  |  14.0 KB  |  547 lines

  1. /*
  2.  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
  3.  * unrestricted use provided that this legend is included on all tape
  4.  * media and as a part of the software program in whole or part.  Users
  5.  * may copy or modify Sun RPC without charge, but are not authorized
  6.  * to license or distribute it to anyone else except as part of a product or
  7.  * program developed by the user.
  8.  * 
  9.  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
  10.  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
  11.  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
  12.  * 
  13.  * Sun RPC is provided with no support and without any obligation on the
  14.  * part of Sun Microsystems, Inc. to assist in its use, correction,
  15.  * modification or enhancement.
  16.  * 
  17.  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
  18.  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
  19.  * OR ANY PART THEREOF.
  20.  * 
  21.  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
  22.  * or profits or other special, indirect and consequential damages, even if
  23.  * Sun has been advised of the possibility of such damages.
  24.  * 
  25.  * Sun Microsystems, Inc.
  26.  * 2550 Garcia Avenue
  27.  * Mountain View, California  94043
  28.  */
  29. #ifndef lint
  30. static char sccsid[] = "@(#)rpc_prot.c 1.1 86/02/03 Copyr 1984 Sun Micro";
  31. #endif
  32.  
  33. /*
  34.  * rpc_prot.c
  35.  *
  36.  * Copyright (C) 1984, Sun Microsystems, Inc.
  37.  *
  38.  * This set of routines implements the rpc message definition,
  39.  * its serializer and some common rpc utility routines.
  40.  * The routines are meant for various implementations of rpc -
  41.  * they are NOT for the rpc client or rpc service implementations!
  42.  * Because authentication stuff is easy and is part of rpc, the opaque
  43.  * routines are also in this program.
  44.  */
  45.  
  46. #include <sys/param.h>
  47. #include "types.h"
  48. #include "xdr.h"
  49. #include "auth.h"
  50. #include "clnt.h"
  51. #include "rpc_msg.h"
  52. #include <netinet/in.h>
  53.  
  54. /* * * * * * * * * * * * * * XDR Authentication * * * * * * * * * * * */
  55.  
  56. struct opaque_auth _null_auth;
  57.  
  58. /*
  59.  * XDR an opaque authentication struct
  60.  * (see auth.h)
  61.  */
  62. bool_t
  63. xdr_opaque_auth(xdrs, ap)
  64.     register XDR *xdrs;
  65.     register struct opaque_auth *ap;
  66. {
  67.  
  68.     if (xdr_enum(xdrs, &(ap->oa_flavor)))
  69.         return (xdr_bytes(xdrs, &ap->oa_base,
  70.             &ap->oa_length, MAX_AUTH_BYTES));
  71.     return (FALSE);
  72. }
  73.  
  74. /*
  75.  * XDR a DES key.
  76.  */
  77. bool_t
  78. xdr_deskey(xdrs, blkp)
  79.     register XDR *xdrs;
  80.     register union des_block *blkp;
  81. {
  82.  
  83.     if (! xdr_u_long(xdrs, &(blkp->key.high)))
  84.         return (FALSE);
  85.     return (xdr_u_long(xdrs, &(blkp->key.low)));
  86. }
  87.  
  88. /* * * * * * * * * * * * * * XDR RPC MESSAGE * * * * * * * * * * * * * * * */
  89.  
  90. /*
  91.  * XDR the MSG_ACCEPTED part of a reply message union
  92.  */
  93. bool_t 
  94. xdr_accepted_reply(xdrs, ar)
  95.     register XDR *xdrs;   
  96.     register struct accepted_reply *ar;
  97. {
  98.  
  99.     /* personalized union, rather than calling xdr_union */
  100.     if (! xdr_opaque_auth(xdrs, &(ar->ar_verf)))
  101.         return (FALSE);
  102.     if (! xdr_enum(xdrs, (enum_t *)&(ar->ar_stat)))
  103.         return (FALSE);
  104.     switch (ar->ar_stat) {
  105.  
  106.     case SUCCESS:
  107.         return ((*(ar->ar_results.proc))(xdrs, ar->ar_results.where));
  108.     
  109.     case PROG_MISMATCH:
  110.         if (! xdr_u_long(xdrs, &(ar->ar_vers.low)))
  111.             return (FALSE);
  112.         return (xdr_u_long(xdrs, &(ar->ar_vers.high)));
  113.     }
  114.     return (TRUE);  /* TRUE => open ended set of problems */
  115. }
  116.  
  117. /*
  118.  * XDR the MSG_DENIED part of a reply message union
  119.  */
  120. bool_t 
  121. xdr_rejected_reply(xdrs, rr)
  122.     register XDR *xdrs;
  123.     register struct rejected_reply *rr;
  124. {
  125.  
  126.     /* personalized union, rather than calling xdr_union */
  127.     if (! xdr_enum(xdrs, (enum_t *)&(rr->rj_stat)))
  128.         return (FALSE);
  129.     switch (rr->rj_stat) {
  130.  
  131.     case RPC_MISMATCH:
  132.         if (! xdr_u_long(xdrs, &(rr->rj_vers.low)))
  133.             return (FALSE);
  134.         return (xdr_u_long(xdrs, &(rr->rj_vers.high)));
  135.  
  136.     case AUTH_ERROR:
  137.         return (xdr_enum(xdrs, (enum_t *)&(rr->rj_why)));
  138.     }
  139.     return (FALSE);
  140. }
  141.  
  142. #define    RNDUP(x)  ((((x) + BYTES_PER_XDR_UNIT - 1) / BYTES_PER_XDR_UNIT) \
  143.            * BYTES_PER_XDR_UNIT)
  144.  
  145. static struct xdr_discrim reply_dscrm[3] = {
  146.     { (int)MSG_ACCEPTED, xdr_accepted_reply },
  147.     { (int)MSG_DENIED, xdr_rejected_reply },
  148.     { __dontcare__, NULL_xdrproc_t } };
  149.  
  150. /*
  151.  * XDR a reply message
  152.  */
  153. bool_t
  154. xdr_replymsg(xdrs, rmsg)
  155.     register XDR *xdrs;
  156.     register struct rpc_msg *rmsg;
  157. {
  158.     register long *buf;
  159.     register struct accepted_reply *ar;
  160.     register struct opaque_auth *oa;
  161.  
  162.     if (xdrs->x_op == XDR_ENCODE &&
  163.         rmsg->rm_reply.rp_stat == MSG_ACCEPTED &&
  164.         rmsg->rm_direction == REPLY &&
  165.         (buf = XDR_INLINE(xdrs, 6 * BYTES_PER_XDR_UNIT +
  166.         rmsg->rm_reply.rp_acpt.ar_verf.oa_length)) != NULL) {
  167.         IXDR_PUT_LONG(buf, rmsg->rm_xid);
  168.         IXDR_PUT_ENUM(buf, rmsg->rm_direction);
  169.         IXDR_PUT_ENUM(buf, rmsg->rm_reply.rp_stat);
  170.         ar = &rmsg->rm_reply.rp_acpt;
  171.         oa = &ar->ar_verf;
  172.         IXDR_PUT_ENUM(buf, oa->oa_flavor);
  173.         IXDR_PUT_LONG(buf, oa->oa_length);
  174.         if (oa->oa_length) {
  175.             bcopy(oa->oa_base, buf, oa->oa_length);
  176.             buf += (oa->oa_length +
  177.                 BYTES_PER_XDR_UNIT - 1) /
  178.                 sizeof (long);
  179.         }
  180.         /*
  181.          * stat and rest of reply, copied from xdr_accepted_reply
  182.          */
  183.         IXDR_PUT_ENUM(buf, ar->ar_stat);
  184.         switch (ar->ar_stat) {
  185.  
  186.         case SUCCESS:
  187.             return ((*(ar->ar_results.proc))
  188.                 (xdrs, ar->ar_results.where));
  189.     
  190.         case PROG_MISMATCH:
  191.             if (! xdr_u_long(xdrs, &(ar->ar_vers.low)))
  192.                 return (FALSE);
  193.             return (xdr_u_long(xdrs, &(ar->ar_vers.high)));
  194.         }
  195.         return (TRUE);
  196.     }
  197.     if (xdrs->x_op == XDR_DECODE &&
  198.         (buf = XDR_INLINE(xdrs, 3 * BYTES_PER_XDR_UNIT)) != NULL) {
  199.         rmsg->rm_xid = IXDR_GET_LONG(buf);
  200.         rmsg->rm_direction = IXDR_GET_ENUM(buf, enum msg_type);
  201.         if (rmsg->rm_direction != REPLY) {
  202.             return (FALSE);
  203.         }
  204.         rmsg->rm_reply.rp_stat = IXDR_GET_ENUM(buf, enum reply_stat);
  205.         if (rmsg->rm_reply.rp_stat != MSG_ACCEPTED) {
  206.             if (rmsg->rm_reply.rp_stat == MSG_DENIED) {
  207.                 return (xdr_rejected_reply(xdrs,
  208.                     &rmsg->rm_reply.rp_rjct));
  209.             }
  210.             return (FALSE);
  211.         }
  212.         ar = &rmsg->rm_reply.rp_acpt;
  213.         oa = &ar->ar_verf;
  214.         buf = XDR_INLINE(xdrs, 2 * BYTES_PER_XDR_UNIT);
  215.         if (buf != NULL) {
  216.             oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t);
  217.             oa->oa_length = IXDR_GET_LONG(buf);
  218.         } else {
  219.             if (xdr_enum(xdrs, &oa->oa_flavor) == FALSE ||
  220.                 xdr_u_int(xdrs, &oa->oa_length) == FALSE) {
  221.                 return (FALSE);
  222.             }
  223.         }
  224.         if (oa->oa_length) {
  225.             if (oa->oa_length > MAX_AUTH_BYTES) {
  226.                 return (FALSE);
  227.             }
  228.             if (oa->oa_base == NULL) {
  229.                 oa->oa_base = (caddr_t)
  230.                     mem_alloc(oa->oa_length);
  231.             }
  232.             buf = XDR_INLINE(xdrs, RNDUP(oa->oa_length));
  233.             if (buf == NULL) {
  234.                 if (xdr_opaque(xdrs, oa->oa_base,
  235.                     oa->oa_length) == FALSE) {
  236.                     return (FALSE);
  237.                 }
  238.             } else {
  239.                 bcopy(buf, oa->oa_base, oa->oa_length);
  240.                 /* no real need....
  241.                 buf += RNDUP(oa->oa_length) / sizeof (long);
  242.                 */
  243.             }
  244.         }
  245.         /*
  246.          * stat and rest of reply, copied from
  247.          * xdr_accepted_reply
  248.          */
  249.         xdr_enum(xdrs, &ar->ar_stat);
  250.         switch (ar->ar_stat) {
  251.  
  252.         case SUCCESS:
  253.             return ((*(ar->ar_results.proc))
  254.                 (xdrs, ar->ar_results.where));
  255.  
  256.         case PROG_MISMATCH:
  257.             if (! xdr_u_long(xdrs, &(ar->ar_vers.low)))
  258.                 return (FALSE);
  259.             return (xdr_u_long(xdrs, &(ar->ar_vers.high)));
  260.         }
  261.         return (TRUE);
  262.     }
  263.     if (
  264.         xdr_u_long(xdrs, &(rmsg->rm_xid)) && 
  265.         xdr_enum(xdrs, (enum_t *)&(rmsg->rm_direction)) &&
  266.         (rmsg->rm_direction == REPLY) )
  267.         return (xdr_union(xdrs, (enum_t *)&(rmsg->rm_reply.rp_stat),
  268.             (caddr_t)&(rmsg->rm_reply.ru), reply_dscrm, NULL_xdrproc_t));
  269.     return (FALSE);
  270. }
  271.  
  272. /*
  273.  * XDR a call message
  274.  */
  275. bool_t
  276. xdr_callmsg(xdrs, cmsg)
  277.     register XDR *xdrs;
  278.     register struct rpc_msg *cmsg;
  279. {
  280.     register long *buf;
  281.     register struct opaque_auth *oa;
  282.  
  283.     if (xdrs->x_op == XDR_ENCODE) {
  284.         if (cmsg->rm_call.cb_cred.oa_length > MAX_AUTH_BYTES) {
  285.             return (FALSE);
  286.         }
  287.         if (cmsg->rm_call.cb_verf.oa_length > MAX_AUTH_BYTES) {
  288.             return (FALSE);
  289.         }
  290.         buf = XDR_INLINE(xdrs, 8 * BYTES_PER_XDR_UNIT
  291.             + RNDUP(cmsg->rm_call.cb_cred.oa_length)
  292.             + 2 * BYTES_PER_XDR_UNIT
  293.             + RNDUP(cmsg->rm_call.cb_verf.oa_length));
  294.         if (buf != NULL) {
  295.             IXDR_PUT_LONG(buf, cmsg->rm_xid);
  296.             IXDR_PUT_ENUM(buf, cmsg->rm_direction);
  297.             if (cmsg->rm_direction != CALL) {
  298.                 return (FALSE);
  299.             }
  300.             IXDR_PUT_LONG(buf, cmsg->rm_call.cb_rpcvers);
  301.             if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) {
  302.                 return (FALSE);
  303.             }
  304.             IXDR_PUT_LONG(buf, cmsg->rm_call.cb_prog);
  305.             IXDR_PUT_LONG(buf, cmsg->rm_call.cb_vers);
  306.             IXDR_PUT_LONG(buf, cmsg->rm_call.cb_proc);
  307.             oa = &cmsg->rm_call.cb_cred;
  308.             IXDR_PUT_ENUM(buf, oa->oa_flavor);
  309.             IXDR_PUT_LONG(buf, oa->oa_length);
  310.             if (oa->oa_length) {
  311.                 bcopy(oa->oa_base, buf, oa->oa_length);
  312.                 buf += RNDUP(oa->oa_length) / sizeof (long);
  313.             }
  314.             oa = &cmsg->rm_call.cb_verf;
  315.             IXDR_PUT_ENUM(buf, oa->oa_flavor);
  316.             IXDR_PUT_LONG(buf, oa->oa_length);
  317.             if (oa->oa_length) {
  318.                 bcopy(oa->oa_base, buf, oa->oa_length);
  319.                 /* no real need....
  320.                 buf += RNDUP(oa->oa_length) / sizeof (long);
  321.                 */
  322.             }
  323.             return (TRUE);
  324.         }
  325.     }
  326.     if (xdrs->x_op == XDR_DECODE) {
  327.         buf = XDR_INLINE(xdrs, 8 * BYTES_PER_XDR_UNIT);
  328.         if (buf != NULL) {
  329.             cmsg->rm_xid = IXDR_GET_LONG(buf);
  330.             cmsg->rm_direction = IXDR_GET_ENUM(buf, enum msg_type);
  331.             if (cmsg->rm_direction != CALL) {
  332.                 return (FALSE);
  333.             }
  334.             cmsg->rm_call.cb_rpcvers = IXDR_GET_LONG(buf);
  335.             if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) {
  336.                 return (FALSE);
  337.             }
  338.             cmsg->rm_call.cb_prog = IXDR_GET_LONG(buf);
  339.             cmsg->rm_call.cb_vers = IXDR_GET_LONG(buf);
  340.             cmsg->rm_call.cb_proc = IXDR_GET_LONG(buf);
  341.             oa = &cmsg->rm_call.cb_cred;
  342.             oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t);
  343.             oa->oa_length = IXDR_GET_LONG(buf);
  344.             if (oa->oa_length) {
  345.                 if (oa->oa_length > MAX_AUTH_BYTES) {
  346.                     return (FALSE);
  347.                 }
  348.                 if (oa->oa_base == NULL) {
  349.                     oa->oa_base = (caddr_t)
  350.                         mem_alloc(oa->oa_length);
  351.                 }
  352.                 buf = XDR_INLINE(xdrs, RNDUP(oa->oa_length));
  353.                 if (buf == NULL) {
  354.                     if (xdr_opaque(xdrs, oa->oa_base,
  355.                         oa->oa_length) == FALSE) {
  356.                         return (FALSE);
  357.                     }
  358.                 } else {
  359.                     bcopy(buf, oa->oa_base, oa->oa_length);
  360.                     /* no real need....
  361.                     buf += RNDUP(oa->oa_length) /
  362.                         sizeof (long);
  363.                     */
  364.                 }
  365.             }
  366.             oa = &cmsg->rm_call.cb_verf;
  367.             buf = XDR_INLINE(xdrs, 2 * BYTES_PER_XDR_UNIT);
  368.             if (buf == NULL) {
  369.                 if (xdr_enum(xdrs, &oa->oa_flavor) == FALSE ||
  370.                     xdr_u_int(xdrs, &oa->oa_length) == FALSE) {
  371.                     return (FALSE);
  372.                 }
  373.             } else {
  374.                 oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t);
  375.                 oa->oa_length = IXDR_GET_LONG(buf);
  376.             }
  377.             if (oa->oa_length) {
  378.                 if (oa->oa_length > MAX_AUTH_BYTES) {
  379.                     return (FALSE);
  380.                 }
  381.                 if (oa->oa_base == NULL) {
  382.                     oa->oa_base = (caddr_t)
  383.                         mem_alloc(oa->oa_length);
  384.                 }
  385.                 buf = XDR_INLINE(xdrs, RNDUP(oa->oa_length));
  386.                 if (buf == NULL) {
  387.                     if (xdr_opaque(xdrs, oa->oa_base,
  388.                         oa->oa_length) == FALSE) {
  389.                         return (FALSE);
  390.                     }
  391.                 } else {
  392.                     bcopy(buf, oa->oa_base, oa->oa_length);
  393.                     /* no real need...
  394.                     buf += RNDUP(oa->oa_length) /
  395.                         sizeof (long);
  396.                     */
  397.                 }
  398.             }
  399.             return (TRUE);
  400.         }
  401.     }
  402.     if (
  403.         xdr_u_long(xdrs, &(cmsg->rm_xid)) &&
  404.         xdr_enum(xdrs, (enum_t *)&(cmsg->rm_direction)) &&
  405.         (cmsg->rm_direction == CALL) &&
  406.         xdr_u_long(xdrs, &(cmsg->rm_call.cb_rpcvers)) &&
  407.         (cmsg->rm_call.cb_rpcvers == RPC_MSG_VERSION) &&
  408.         xdr_u_long(xdrs, &(cmsg->rm_call.cb_prog)) &&
  409.         xdr_u_long(xdrs, &(cmsg->rm_call.cb_vers)) &&
  410.         xdr_u_long(xdrs, &(cmsg->rm_call.cb_proc)) &&
  411.         xdr_opaque_auth(xdrs, &(cmsg->rm_call.cb_cred)) )
  412.         return (xdr_opaque_auth(xdrs, &(cmsg->rm_call.cb_verf)));
  413.     return (FALSE);
  414. }
  415.  
  416. /*
  417.  * Serializes the "static part" of a call message header.
  418.  * The fields include: rm_xid, rm_direction, rpcvers, prog, and vers.
  419.  * The rm_xid is not really static, but the user can easily munge on the fly.
  420.  */
  421. bool_t
  422. xdr_callhdr(xdrs, cmsg)
  423.     register XDR *xdrs;
  424.     register struct rpc_msg *cmsg;
  425. {
  426.  
  427.     cmsg->rm_direction = CALL;
  428.     cmsg->rm_call.cb_rpcvers = RPC_MSG_VERSION;
  429.     if (
  430.         (xdrs->x_op == XDR_ENCODE) &&
  431.         xdr_u_long(xdrs, &(cmsg->rm_xid)) &&
  432.         xdr_enum(xdrs, (enum_t *)&(cmsg->rm_direction)) &&
  433.         xdr_u_long(xdrs, &(cmsg->rm_call.cb_rpcvers)) &&
  434.         xdr_u_long(xdrs, &(cmsg->rm_call.cb_prog)) )
  435.         return (xdr_u_long(xdrs, &(cmsg->rm_call.cb_vers)));
  436.     return (FALSE);
  437. }
  438.  
  439. /* ************************** Client utility routine ************* */
  440.  
  441. static void
  442. accepted(acpt_stat, error)
  443.     register enum accept_stat acpt_stat;
  444.     register struct rpc_err *error;
  445. {
  446.  
  447.     switch (acpt_stat) {
  448.  
  449.     case PROG_UNAVAIL:
  450.         error->re_status = RPC_PROGUNAVAIL;
  451.         return;
  452.  
  453.     case PROG_MISMATCH:
  454.         error->re_status = RPC_PROGVERSMISMATCH;
  455.         return;
  456.  
  457.     case PROC_UNAVAIL:
  458.         error->re_status = RPC_PROCUNAVAIL;
  459.         return;
  460.  
  461.     case GARBAGE_ARGS:
  462.         error->re_status = RPC_CANTDECODEARGS;
  463.         return;
  464.  
  465.     case SYSTEM_ERR:
  466.         error->re_status = RPC_SYSTEMERROR;
  467.         return;
  468.  
  469.     case SUCCESS:
  470.         error->re_status = RPC_SUCCESS;
  471.         return;
  472.     }
  473.     /* something's wrong, but we don't know what ... */
  474.     error->re_status = RPC_FAILED;
  475.     error->re_lb.s1 = (long)MSG_ACCEPTED;
  476.     error->re_lb.s2 = (long)acpt_stat;
  477. }
  478.  
  479. static void 
  480. rejected(rjct_stat, error)
  481.     register enum reject_stat rjct_stat;
  482.     register struct rpc_err *error;
  483. {
  484.  
  485.     switch (rjct_stat) {
  486.  
  487.     case RPC_VERSMISMATCH:
  488.         error->re_status = RPC_VERSMISMATCH;
  489.         return;
  490.  
  491.     case AUTH_ERROR:
  492.         error->re_status = RPC_AUTHERROR;
  493.         return;
  494.     }
  495.     /* something's wrong, but we don't know what ... */
  496.     error->re_status = RPC_FAILED;
  497.     error->re_lb.s1 = (long)MSG_DENIED;
  498.     error->re_lb.s2 = (long)rjct_stat;
  499. }
  500.  
  501. /*
  502.  * given a reply message, fills in the error
  503.  */
  504. void
  505. _seterr_reply(msg, error)
  506.     register struct rpc_msg *msg;
  507.     register struct rpc_err *error;
  508. {
  509.  
  510.     /* optimized for normal, SUCCESSful case */
  511.     switch (msg->rm_reply.rp_stat) {
  512.  
  513.     case MSG_ACCEPTED:
  514.         if (msg->acpted_rply.ar_stat == SUCCESS) {
  515.             error->re_status = RPC_SUCCESS;
  516.             return;
  517.         };
  518.         accepted(msg->acpted_rply.ar_stat, error);
  519.         break;
  520.  
  521.     case MSG_DENIED:
  522.         rejected(msg->rjcted_rply.rj_stat, error);
  523.         break;
  524.  
  525.     default:
  526.         error->re_status = RPC_FAILED;
  527.         error->re_lb.s1 = (long)(msg->rm_reply.rp_stat);
  528.         break;
  529.     }
  530.     switch (error->re_status) {
  531.  
  532.     case RPC_VERSMISMATCH:
  533.         error->re_vers.low = msg->rjcted_rply.rj_vers.low;
  534.         error->re_vers.high = msg->rjcted_rply.rj_vers.high;
  535.         break;
  536.  
  537.     case RPC_AUTHERROR:
  538.         error->re_why = msg->rjcted_rply.rj_why;
  539.         break;
  540.  
  541.     case RPC_PROGVERSMISMATCH:
  542.         error->re_vers.low = msg->acpted_rply.ar_vers.low;
  543.         error->re_vers.high = msg->acpted_rply.ar_vers.high;
  544.         break;
  545.     }
  546. }
  547.