RReemmoottee PPrroocceedduurree CCaallll PPrrooggrraammmmiinngg GGuuiiddee This document assumes a working knowledge of network theory. It is intended for programmers who wish to write network applications using remote procedure calls (explained below), and who want to understand the RPC mechanisms usually hidden by the _r_p_c_g_e_n_(_1_) protocol compiler. _r_p_c_g_e_n is described in detail in the previous chapter, the rrppccggeenn _P_r_o_g_r_a_m_m_i_n_g _G_u_i_d_e. NNoottee:: _B_e_f_o_r_e _a_t_t_e_m_p_t_i_n_g _t_o _w_r_i_t_e _a _n_e_t_w_o_r_k _a_p_p_l_i_c_a_t_i_o_n_, _o_r _t_o _c_o_n_v_e_r_t _a_n _e_x_i_s_t_i_n_g _n_o_n_-_n_e_t_w_o_r_k _a_p_p_l_i_c_a_t_i_o_n _t_o _r_u_n _o_v_e_r _t_h_e _n_e_t_w_o_r_k_, _y_o_u _m_a_y _w_a_n_t _t_o _u_n_d_e_r_s_t_a_n_d _t_h_e _m_a_t_e_r_i_a_l _i_n _t_h_i_s _c_h_a_p_t_e_r_. _H_o_w_e_v_e_r_, _f_o_r _m_o_s_t _a_p_p_l_i_c_a_t_i_o_n_s_, _y_o_u _c_a_n _c_i_r_c_u_m_v_e_n_t _t_h_e _n_e_e_d _t_o _c_o_p_e _w_i_t_h _t_h_e _d_e_t_a_i_l_s _p_r_e_s_e_n_t_e_d _h_e_r_e _b_y _u_s_i_n_g _r_p_c_g_e_n_. _T_h_e _G_e_n_e_r_a_t_i_n_g _X_D_R _R_o_u_t_i_n_e_s _s_e_c_t_i_o_n _o_f _t_h_a_t _c_h_a_p_t_e_r _c_o_n_t_a_i_n_s _t_h_e _c_o_m_p_l_e_t_e _s_o_u_r_c_e _f_o_r _a _w_o_r_k_i_n_g _R_P_C _s_e_r_v_i_c_e_-_-_a _r_e_m_o_t_e _d_i_r_e_c_t_o_r_y _l_i_s_t_i_n_g _s_e_r_v_i_c_e _w_h_i_c_h _u_s_e_s _r_p_c_g_e_n _t_o _g_e_n_e_r_- _a_t_e _X_D_R _r_o_u_t_i_n_e_s _a_s _w_e_l_l _a_s _c_l_i_e_n_t _a_n_d _s_e_r_v_e_r _s_t_u_b_s_. What are remote procedure calls? Simply put, they are the high-level communications paradigm used in the operating system. RPC presumes the existence of low-level networking mechanisms (such as TCP/IP and UDP/IP), and upon them it implements a logical client to server communications system designed specifically for the support of network applica- tions. With RPC, the client makes a procedure call to send a data packet to the server. When the packet arrives, the server calls a dispatch routine, performs whatever service is requested, sends back the reply, and the procedure call returns to the client. 11.. LLaayyeerrss ooff RRPPCC The RPC interface can be seen as being divided into three layers.1 _T_h_e _H_i_g_h_e_s_t _L_a_y_e_r_: The highest layer is totally transparent to the operating system, machine and network upon which is is run. It's probably best to think of this level as a way of _u_s_i_n_g RPC, rather than as a _p_a_r_t _o_f RPC proper. Program- mers who write RPC routines should (almost) always make this layer available to others by way of a simple C front end that entirely hides the networking. To illustrate, at this level a program can simply make a call to _r_n_u_s_e_r_s(), a C routine which returns the number of users on a remote machine. The user is not explicitly aware of using RPC -- they simply call a procedure, just as they ----------- 1 For a complete specification of the routines in the remote procedure call Library, see the _r_p_c_(_3_N_) manual page. - 1 - Page 2 Remote Procedure Call Programming Guide would call _m_a_l_l_o_c_(_). _T_h_e _M_i_d_d_l_e _L_a_y_e_r_: The middle layer is really "RPC proper." Here, the user doesn't need to consider details about sock- ets, the UNIX system, or other low-level implementation mechanisms. They simply make remote procedure calls to rou- tines on other machines. The selling point here is simplic- ity. It's this layer that allows RPC to pass the "hello world" test -- simple things should be simple. The middle- layer routines are used for most applications. RPC calls are made with the system routines _r_e_g_i_s_t_e_r_r_p_c_(_) _c_a_l_l_r_p_c_(_) and _s_v_c___r_u_n(). The first two of these are the most fundamental: _r_e_g_i_s_t_e_r_r_p_c_(_) obtains a unique system-wide procedure-identification number, and _c_a_l_l_r_p_c_(_) actually exe- cutes a remote procedure call. At the middle level, a call to _r_n_u_s_e_r_s_(_) is implemented by way of these two routines. The middle layer is unfortunately rarely used in serious programming due to its inflexibility (simplicity). It does not allow timeout specifications or the choice of transport. It allows no UNIX process control or flexibility in case of errors. It doesn't support multiple kinds of call authenti- cation. The programmer rarely needs all these kinds of con- trol, but one or two of them is often necessary. _T_h_e _L_o_w_e_s_t _L_a_y_e_r_: The lowest layer does allow these details to be controlled by the programmer, and for that reason it is often necessary. Programs written at this level are also most efficient, but this is rarely a real issue -- since RPC clients and servers rarely generate heavy network loads. Although this document only discusses the interface to C, remote procedure calls can be made from any language. Even though this document discusses RPC when it is used to commu- nicate between processes on different machines, it works just as well for communication between different processes on the same machine. Remote Procedure Call Programming Guide Page 3 11..11.. TThhee RRPPCC PPaarraaddiiggmm Here is a diagram of the RPC paradigm: FFiigguurree 11--11 _N_e_t_w_o_r_k _C_o_m_m_u_n_i_c_a_t_i_o_n _w_i_t_h _t_h_e _R_e_m_o_t_e _R_e_o_c_e_d_u_r_e _C_a_l_l + + + | + + | + + | + + client | + service + program | + daemon + | + + +----_c-_a-_l-_l-_r-_p-_c----+---------+ Machine B + function + | + + | + + invoke | + + service | Machine A + + | + + +-----c-a-l-l------+ + + + service | + + + | + + + |service + + + |executes + + + | + + +----_r-_e-_t-_u-_r-_n-----+ + + | answer + + | + + request | + +completed | + + | +----_r-_e-_t-_u-_r-_n-----+---------+ | reply + + | + + program | + + continues | + + | + + | + + + + + Page 4 Remote Procedure Call Programming Guide 22.. HHiigghheerr LLaayyeerrss ooff RRPPCC 22..11.. HHiigghheesstt LLaayyeerr Imagine you're writing a program that needs to know how many users are logged into a remote machine. You can do this by calling the RPC library routine _r_n_u_s_e_r_s_(_) as illustrated below: ##iinncclluuddee <> mmaaiinn((aarrggcc,, aarrggvv)) iinntt aarrggcc;; cchhaarr ****aarrggvv;; {{ iinntt nnuumm;; iiff ((aarrggcc !!== 22)) {{ ffpprriinnttff((ssttddeerrrr,, ""uussaaggee:: rrnnuusseerrss hhoossttnnaammee\\nn""));; eexxiitt((11));; }} iiff ((((nnuumm == rrnnuusseerrss((aarrggvv[[11]])))) << 00)) {{ ffpprriinnttff((ssttddeerrrr,, ""eerrrroorr:: rrnnuusseerrss\\nn""));; eexxiitt((--11));; }} pprriinnttff((""%%dd uusseerrss oonn %%ss\\nn"",, nnuumm,, aarrggvv[[11]]));; eexxiitt((00));; }} RPC library routines such as _r_n_u_s_e_r_s_(_) are in the RPC ser- vices library _l_i_b_r_p_c_s_v_c_._a Thus, the program above should be compiled with %% cccc _p_r_o_g_r_a_m_._c _-_l_r_p_c_s_v_c _r_n_u_s_e_r_s(), like the other RPC library routines, is docu- mented in section 3R of the _S_y_s_t_e_m _I_n_t_e_r_f_a_c_e _M_a_n_u_a_l _f_o_r _t_h_e _S_u_n _W_o_r_k_s_t_a_t_i_o_n, the same section which documents the stan- dard Sun RPC services. See the _i_n_t_r_o_(_3_R_) manual page for an explanation of the documentation strategy for these services and their RPC protocols. Here are some of the RPC service library routines available to the C programmer: TTaabbllee 33--33 _R_P_C _S_e_r_v_i_c_e _L_i_b_r_a_r_y _R_o_u_t_i_n_e_s_._T_S Remote Procedure Call Programming Guide Page 5 +------------------------------------------------------------+ |_R_o_u_t_i_n_e _D_e_s_c_r_i_p_t_i_o_n | +------------------------------------------------------------+ |rnusers _R_e_t_u_r_n _n_u_m_b_e_r _o_f _u_s_e_r_s _o_n _r_e_m_o_t_e _m_a_c_h_i_n_e | |rusers _R_e_t_u_r_n _i_n_f_o_r_m_a_t_i_o_n _a_b_o_u_t _u_s_e_r_s _o_n _r_e_m_o_t_e _m_a_c_h_i_n_e | |havedisk _D_e_t_e_r_m_i_n_e _i_f _r_e_m_o_t_e _m_a_c_h_i_n_e _h_a_s _d_i_s_k | |rstats _G_e_t _p_e_r_f_o_r_m_a_n_c_e _d_a_t_a _f_r_o_m _r_e_m_o_t_e _k_e_r_n_e_l | |rwall _W_r_i_t_e _t_o _s_p_e_c_i_f_i_e_d _r_e_m_o_t_e _m_a_c_h_i_n_e_s | |yppasswd _U_p_d_a_t_e _u_s_e_r _p_a_s_s_w_o_r_d _i_n _Y_e_l_l_o_w _P_a_g_e_s | +------------------------------------------------------------+ Other RPC services -- for example _e_t_h_e_r_(_) _m_o_u_n_t _r_q_u_o_t_a_(_) and _s_p_r_a_y -- are not available to the C programmer as library routines. They do, however, have RPC program numbers so they can be invoked with _c_a_l_l_r_p_c_(_) which will be discussed in the next section. Most of them also have compilable _r_p_c_- _g_e_n_(_1_) protocol description files. (The _r_p_c_g_e_n protocol compiler radically simplifies the process of developing net- work applications. See the rrppccggeenn _P_r_o_g_r_a_m_m_i_n_g _G_u_i_d_e for detailed information about _r_p_c_g_e_n and _r_p_c_g_e_n protocol description files). Page 6 Remote Procedure Call Programming Guide 22..22.. IInntteerrmmeeddiiaattee LLaayyeerr The simplest interface, which explicitly makes RPC calls, uses the functions _c_a_l_l_r_p_c_(_) and _r_e_g_i_s_t_e_r_r_p_c_(_) Using this method, the number of remote users can be gotten as follows: #include #include #include #include main(argc, argv) int argc; char **argv; { unsigned long nusers; int stat; if (argc != 2) { fprintf(stderr, "usage: nusers hostname\n"); exit(-1); } if (stat = callrpc(argv[1], RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM, xdr_void, 0, xdr_u_long, &nusers) != 0) { clnt_perrno(stat); exit(1); } printf("%d users on %s\n", nusers, argv[1]); exit(0); } Each RPC procedure is uniquely defined by a program number, version number, and procedure number. The program number specifies a group of related remote procedures, each of which has a different procedure number. Each program also has a version number, so when a minor change is made to a remote service (adding a new procedure, for example), a new program number doesn't have to be assigned. When you want to call a procedure to find the number of remote users, you look up the appropriate program, version and procedure num- bers in a manual, just as you look up the name of a memory allocator when you want to allocate memory. The simplest way of making remote procedure calls is with the the RPC library routine _c_a_l_l_r_p_c_(_) It has eight parame- ters. The first is the name of the remote server machine. The next three parameters are the program, version, and pro- cedure numbers--together they identify the procedure to be called. The fifth and sixth parameters are an XDR filter and an argument to be encoded and passed to the remote pro- cedure. The final two parameters are a filter for decoding the results returned by the remote procedure and a pointer to the place where the procedure's results are to be stored. Remote Procedure Call Programming Guide Page 7 Multiple arguments and results are handled by embedding them in structures. If _c_a_l_l_r_p_c_(_) completes successfully, it returns zero; else it returns a nonzero value. The return codes (of type cast into an integer) are found in _<_r_p_c_/_c_l_n_t_._h_>. Since data types may be represented differently on different machines, _c_a_l_l_r_p_c_(_) needs both the type of the RPC argument, as well as a pointer to the argument itself (and similarly for the result). For _R_U_S_E_R_S_P_R_O_C___N_U_M, the return value is an _u_n_s_i_g_n_e_d _l_o_n_g so _c_a_l_l_r_p_c_(_) has _x_d_r___u___l_o_n_g_(_) as its first return parameter, which says that the result is of type _u_n_s_i_g_n_e_d _l_o_n_g and _&_n_u_s_e_r_s as its second return parameter, which is a pointer to where the long result will be placed. Since _R_U_S_E_R_S_P_R_O_C___N_U_M takes no argument, the argument parame- ter of _c_a_l_l_r_p_c_(_) is _x_d_r___v_o_i_d(). After trying several times to deliver a message, if _c_a_l_l_- _r_p_c_(_) gets no answer, it returns with an error code. The delivery mechanism is UDP, which stands for User Datagram Protocol. Methods for adjusting the number of retries or for using a different protocol require you to use the lower layer of the RPC library, discussed later in this document. The remote server procedure corresponding to the above might look like this: cchhaarr ** nnuusseerr((iinnddaattaa)) cchhaarr **iinnddaattaa;; {{ uunnssiiggnneedd lloonngg nnuusseerrss;; _/_* _* _C_o_d_e _h_e_r_e _t_o _c_o_m_p_u_t_e _t_h_e _n_u_m_b_e_r _o_f _u_s_e_r_s _* _a_n_d _p_l_a_c_e _r_e_s_u_l_t _i_n _v_a_r_i_a_b_l_e _n_u_s_e_r_s_. _*_/ rreettuurrnn((((cchhaarr **))&&nnuusseerrss));; }} It takes one argument, which is a pointer to the input of the remote procedure call (ignored in our example), and it returns a pointer to the result. In the current version of C, character pointers are the generic pointers, so both the input argument and the return value are cast to _c_h_a_r _*. Normally, a server registers all of the RPC calls it plans to handle, and then goes into an infinite loop waiting to service requests. In this example, there is only a single procedure to register, so the main body of the server would look like this: Page 8 Remote Procedure Call Programming Guide ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> cchhaarr **nnuusseerr(());; mmaaiinn(()) {{ rreeggiisstteerrrrppcc((RRUUSSEERRSSPPRROOGG,, RRUUSSEERRSSVVEERRSS,, RRUUSSEERRSSPPRROOCC__NNUUMM,, nnuusseerr,, xxddrr__vvooiidd,, xxddrr__uu__lloonngg));; ssvvcc__rruunn(());; //** _N_e_v_e_r _r_e_t_u_r_n_s **// ffpprriinnttff((ssttddeerrrr,, ""EErrrroorr:: ssvvcc__rruunn rreettuurrnneedd!!\\nn""));; eexxiitt((11));; }} The _r_e_g_i_s_t_e_r_r_p_c_(_) routine registers a C procedure as corre- sponding to a given RPC procedure number. The first three parameters, _R_U_S_E_R_P_R_O_G, _R_U_S_E_R_S_V_E_R_S, and _R_U_S_E_R_S_P_R_O_C___N_U_M are the program, version, and procedure numbers of the remote procedure to be registered; _n_u_s_e_r_(_) is the name of the local procedure that implements the remote procedure; and _x_d_r___v_o_i_d_(_) and _x_d_r___u___l_o_n_g_(_) are the XDR filters for the remote procedure's arguments and results, respectively. (Multiple arguments or multiple results are passed as struc- tures). Only the UDP transport mechanism can use _r_e_g_i_s_t_e_r_r_p_c_(_) thus, it is always safe in conjunction with calls generated by _c_a_l_l_r_p_c_(_). WWaarrnniinngg:: tthhee UUDDPP ttrraannssppoorrtt mmeecchhaanniissmm ccaann oonnllyy ddeeaall wwiitthh aarrgguummeennttss aanndd rreessuullttss lleessss tthhaann 88KK bbyytteess iinn lleennggtthh.. After registering the local procedure, the server program's main procedure calls _s_v_c___r_u_n(), the RPC library's remote procedure dispatcher. It is this function that calls the remote procedures in response to RPC call messages. Note that the dispatcher takes care of decoding remote procedure arguments and encoding results, using the XDR filters speci- fied when the remote procedure was registered. 22..33.. AAssssiiggnniinngg PPrrooggrraamm NNuummbbeerrss Program numbers are assigned in groups of _0_x_2_0_0_0_0_0_0_0 accord- ing to the following chart: Remote Procedure Call Programming Guide Page 9 00xx00 -- 00xx11ffffffffffffff Defined by Sun 00xx2200000000000000 -- 00xx33ffffffffffffff Defined by user 00xx4400000000000000 -- 00xx55ffffffffffffff Transient 00xx6600000000000000 -- 00xx77ffffffffffffff Reserved 00xx8800000000000000 -- 00xx99ffffffffffffff Reserved 00xxaa00000000000000 -- 00xxbbffffffffffffff Reserved 00xxcc00000000000000 -- 00xxddffffffffffffff Reserved 00xxee00000000000000 -- 00xxffffffffffffffff Reserved Sun Microsystems administers the first group of numbers, which should be identical for all Sun customers. If a cus- tomer develops an application that might be of general interest, that application should be given an assigned num- ber in the first range. The second group of numbers is reserved for specific customer applications. This range is intended primarily for debugging new programs. The third group is reserved for applications that generate program numbers dynamically. The final groups are reserved for future use, and should not be used. To register a protocol specification, send a request by net- work mail to _r_p_c_@_s_u_n or write to: RPC Administrator Sun Microsystems 2550 Garcia Ave. Mountain View, CA 94043 Please include a compilable _r_p_c_g_e_n ".x" file describing your protocol. You will be given a unique program number in return. The RPC program numbers and protocol specifications of stan- dard Sun RPC services can be found in the include files in _/_u_s_r_/_i_n_c_l_u_d_e_/_r_p_c_s_v_c. These services, however, constitute only a small subset of those which have been registered. The complete list of registered programs, as of the time when this manual was printed, is: TTaabbllee 33--22 _R_P_C _R_e_g_i_s_t_e_r_e_d _P_r_o_g_r_a_m_s +------------------------------------------------------------+ |_RR_PP_CC _NN_uu_mm_bb_ee_rr _PP_rr_oo_gg_rr_aa_mm _DD_ee_ss_cc_rr_ii_pp_tt_ii_oo_nn | +------------------------------------------------------------+ |100000 PMAPPROG _p_o_r_t_m_a_p_p_e_r | |100001 RSTATPROG _r_e_m_o_t_e _s_t_a_t_s | |100002 RUSERSPROG _r_e_m_o_t_e _u_s_e_r_s | |100003 NFSPROG _n_f_s | |100004 YPPROG _Y_e_l_l_o_w _P_a_g_e_s | |100005 MOUNTPROG _m_o_u_n_t _d_e_m_o_n | |100006 DBXPROG _r_e_m_o_t_e _d_b_x | |100007 YPBINDPROG _y_p _b_i_n_d_e_r | |100008 WALLPROG _s_h_u_t_d_o_w_n _m_s_g | +------------------------------------------------------------+ Page 10 Remote Procedure Call Programming Guide +------------------------------------------------------------+ |_RR_PP_CC _NN_uu_mm_bb_ee_rr _PP_rr_oo_gg_rr_aa_mm _DD_ee_ss_cc_rr_ii_pp_tt_ii_oo_nn | +------------------------------------------------------------+ |100009 YPPASSWDPROG _y_p_p_a_s_s_w_d _s_e_r_v_e_r | |100010 ETHERSTATPROG _e_t_h_e_r _s_t_a_t_s | |100011 RQUOTAPROG _d_i_s_k _q_u_o_t_a_s | |100012 SPRAYPROG _s_p_r_a_y _p_a_c_k_e_t_s | |100013 IBM3270PROG _3_2_7_0 _m_a_p_p_e_r | |100014 IBMRJEPROG _R_J_E _m_a_p_p_e_r | |100015 SELNSVCPROG _s_e_l_e_c_t_i_o_n _s_e_r_v_i_c_e | |100016 RDATABASEPROG _r_e_m_o_t_e _d_a_t_a_b_a_s_e _a_c_c_e_s_s | |100017 REXECPROG _r_e_m_o_t_e _e_x_e_c_u_t_i_o_n | |100018 ALICEPROG _A_l_i_c_e _O_f_f_i_c_e _A_u_t_o_m_a_t_i_o_n | |100019 SCHEDPROG _s_c_h_e_d_u_l_i_n_g _s_e_r_v_i_c_e | |100020 LOCKPROG _l_o_c_a_l _l_o_c_k _m_a_n_a_g_e_r | |100021 NETLOCKPROG _n_e_t_w_o_r_k _l_o_c_k _m_a_n_a_g_e_r | |100022 X25PROG _x_._2_5 _i_n_r _p_r_o_t_o_c_o_l | |100023 STATMON1PROG _s_t_a_t_u_s _m_o_n_i_t_o_r _1 | |100024 STATMON2PROG _s_t_a_t_u_s _m_o_n_i_t_o_r _2 | |100025 SELNLIBPROG _s_e_l_e_c_t_i_o_n _l_i_b_r_a_r_y | |100026 BOOTPARAMPROG _b_o_o_t _p_a_r_a_m_e_t_e_r_s _s_e_r_v_i_c_e | |100027 MAZEPROG _m_a_z_e_w_a_r_s _g_a_m_e | |100028 YPUPDATEPROG _y_p _u_p_d_a_t_e | |100029 KEYSERVEPROG _k_e_y _s_e_r_v_e_r | |100030 SECURECMDPROG _s_e_c_u_r_e _l_o_g_i_n | |100031 NETFWDIPROG _n_f_s _n_e_t _f_o_r_w_a_r_d_e_r _i_n_i_t | |100032 NETFWDTPROG _n_f_s _n_e_t _f_o_r_w_a_r_d_e_r _t_r_a_n_s | |100033 SUNLINKMAP_PROG _s_u_n_l_i_n_k _M_A_P | |100034 NETMONPROG _n_e_t_w_o_r_k _m_o_n_i_t_o_r | |100035 DBASEPROG _l_i_g_h_t_w_e_i_g_h_t _d_a_t_a_b_a_s_e | |100036 PWDAUTHPROG _p_a_s_s_w_o_r_d _a_u_t_h_o_r_i_z_a_t_i_o_n | |100037 TFSPROG _t_r_a_n_s_l_u_c_e_n_t _f_i_l_e _s_v_c | |100038 NSEPROG _n_s_e _s_e_r_v_e_r | |100039 NSE_ACTIVATE_PROG _n_s_e _a_c_t_i_v_a_t_e _d_a_e_m_o_n | | | |150001 PCNFSDPROG _p_c _p_a_s_s_w_d _a_u_t_h_o_r_i_z_a_t_i_o_n | | | |200000 PYRAMIDLOCKINGPROG _P_y_r_a_m_i_d_-_l_o_c_k_i_n_g | |200001 PYRAMIDSYS5 _P_y_r_a_m_i_d_-_s_y_s_5 | |200002 CADDS_IMAGE _C_V _c_a_d_d_s___i_m_a_g_e | | | |300001 ADT_RFLOCKPROG _A_D_T _f_i_l_e _l_o_c_k_i_n_g | +------------------------------------------------------------+ 22..44.. PPaassssiinngg AArrbbiittrraarryy DDaattaa TTyyppeess In the previous example, the RPC call passes a single _u_n_s_i_g_n_e_d _l_o_n_g RPC can handle arbitrary data structures, regardless of different machines' byte orders or structure layout conventions, by always converting them to a network standard called _E_x_t_e_r_n_a_l _D_a_t_a _R_e_p_r_e_s_e_n_t_a_t_i_o_n (XDR) before sending them over the wire. The process of converting from a particular machine representation to XDR format is called _s_e_r_i_a_l_i_z_i_n_g, and the reverse process is called Remote Procedure Call Programming Guide Page 11 _d_e_s_e_r_i_a_l_i_z_i_n_g. The type field parameters of _c_a_l_l_r_p_c_(_) and _r_e_g_i_s_t_e_r_r_p_c_(_) can be a built-in procedure like _x_d_r___u___l_o_n_g_(_) in the previous example, or a user supplied one. XDR has these built-in type routines: xxddrr__iinntt(()) xxddrr__uu__iinntt(()) xxddrr__eennuumm(()) xxddrr__lloonngg(()) xxddrr__uu__lloonngg(()) xxddrr__bbooooll(()) xxddrr__sshhoorrtt(()) xxddrr__uu__sshhoorrtt(()) xxddrr__wwrraappssttrriinngg(()) xxddrr__cchhaarr(()) xxddrr__uu__cchhaarr(()) Note that the routine _x_d_r___s_t_r_i_n_g_(_) exists, but cannot be used with _c_a_l_l_r_p_c_(_) and _r_e_g_i_s_t_e_r_r_p_c(), which only pass two parameters to their XDR routines. _x_d_r___w_r_a_p_s_t_r_i_n_g_(_) has only two parameters, and is thus OK. It calls _x_d_r___s_t_r_i_n_g(). As an example of a user-defined type routine, if you wanted to send the structure ssttrruucctt ssiimmppllee {{ iinntt aa;; sshhoorrtt bb;; }} ssiimmppllee;; then you would call _c_a_l_l_r_p_c_(_) as ccaallllrrppcc((hhoossttnnaammee,, PPRROOGGNNUUMM,, VVEERRSSNNUUMM,, PPRROOCCNNUUMM,, xxddrr__ssiimmppllee,, &&ssiimmppllee ......));; where _x_d_r___s_i_m_p_l_e_(_) is written as: ##iinncclluuddee <> xxddrr__ssiimmppllee((xxddrrsspp,, ssiimmpplleepp)) XXDDRR **xxddrrsspp;; ssttrruucctt ssiimmppllee **ssiimmpplleepp;; {{ iiff ((!!xxddrr__iinntt((xxddrrsspp,, &&ssiimmpplleepp-->>aa)))) rreettuurrnn ((00));; iiff ((!!xxddrr__sshhoorrtt((xxddrrsspp,, &&ssiimmpplleepp-->>bb)))) rreettuurrnn ((00));; rreettuurrnn ((11));; }} An XDR routine returns nonzero (true in the sense of C) if it completes successfully, and zero otherwise. A complete description of XDR is in the _X_D_R _P_r_o_t_o_c_o_l _S_p_e_c_i_f_i_c_a_t_i_o_n sec- tion of this manual, only few implementation examples are given here. In addition to the built-in primitives, there are also the prefabricated building blocks: Page 12 Remote Procedure Call Programming Guide xxddrr__aarrrraayy(()) xxddrr__bbyytteess(()) xxddrr__rreeffeerreennccee(()) xxddrr__vveeccttoorr(()) xxddrr__uunniioonn(()) xxddrr__ppooiinntteerr(()) xxddrr__ssttrriinngg(()) xxddrr__ooppaaqquuee(()) To send a variable array of integers, you might package them up as a structure like this ssttrruucctt vvaarriinnttaarrrr {{ iinntt **ddaattaa;; iinntt aarrrrllnntthh;; }} aarrrr;; and make an RPC call such as ccaallllrrppcc((hhoossttnnaammee,, PPRROOGGNNUUMM,, VVEERRSSNNUUMM,, PPRROOCCNNUUMM,, xxddrr__vvaarriinnttaarrrr,, &&aarrrr......));; with _x_d_r___v_a_r_i_n_t_a_r_r_(_) defined as: xxddrr__vvaarriinnttaarrrr((xxddrrsspp,, aarrrrpp)) XXDDRR **xxddrrsspp;; ssttrruucctt vvaarriinnttaarrrr **aarrrrpp;; {{ rreettuurrnn ((xxddrr__aarrrraayy((xxddrrsspp,, &&aarrrrpp-->>ddaattaa,, &&aarrrrpp-->>aarrrrllnntthh,, MMAAXXLLEENN,, ssiizzeeooff((iinntt)),, xxddrr__iinntt))));; }} This routine takes as parameters the XDR handle, a pointer to the array, a pointer to the size of the array, the maxi- mum allowable array size, the size of each array element, and an XDR routine for handling each array element. If the size of the array is known in advance, one can use _x_d_r___v_e_c_t_o_r(), which serializes fixed-length arrays. iinntt iinnttaarrrr[[SSIIZZEE]];; xxddrr__iinnttaarrrr((xxddrrsspp,, iinnttaarrrr)) XXDDRR **xxddrrsspp;; iinntt iinnttaarrrr[[]];; {{ iinntt ii;; rreettuurrnn ((xxddrr__vveeccttoorr((xxddrrsspp,, iinnttaarrrr,, SSIIZZEE,, ssiizzeeooff((iinntt)),, xxddrr__iinntt))));; }} XDR always converts quantities to 4-byte multiples when serializing. Thus, if either of the examples above involved characters instead of integers, each character would occupy 32 bits. That is the reason for the XDR routine _x_d_r___b_y_t_e_s_(_) which is like _x_d_r___a_r_r_a_y_(_) except that it packs characters; _x_d_r___b_y_t_e_s_(_) has four parameters, similar to the first four Remote Procedure Call Programming Guide Page 13 parameters of _x_d_r___a_r_r_a_y(). For null-terminated strings, there is also the _x_d_r___s_t_r_i_n_g_(_) routine, which is the same as _x_d_r___b_y_t_e_s_(_) without the length parameter. On serializing it gets the string length from _s_t_r_l_e_n(), and on deserializing it creates a null-terminated string. Here is a final example that calls the previously written _x_d_r___s_i_m_p_l_e_(_) as well as the built-in functions _x_d_r___s_t_r_i_n_g_(_) and _x_d_r___r_e_f_e_r_e_n_c_e(), which chases pointers: ssttrruucctt ffiinnaalleexxaammppllee {{ cchhaarr **ssttrriinngg;; ssttrruucctt ssiimmppllee **ssiimmpplleepp;; }} ffiinnaalleexxaammppllee;; xxddrr__ffiinnaalleexxaammppllee((xxddrrsspp,, ffiinnaallpp)) XXDDRR **xxddrrsspp;; ssttrruucctt ffiinnaalleexxaammppllee **ffiinnaallpp;; {{ iiff ((!!xxddrr__ssttrriinngg((xxddrrsspp,, &&ffiinnaallpp-->>ssttrriinngg,, MMAAXXSSTTRRLLEENN)))) rreettuurrnn ((00));; iiff ((!!xxddrr__rreeffeerreennccee((xxddrrsspp,, &&ffiinnaallpp-->>ssiimmpplleepp,, ssiizzeeooff((ssttrruucctt ssiimmppllee)),, xxddrr__ssiimmppllee));; rreettuurrnn ((00));; rreettuurrnn ((11));; }} Note that we could as easily call _x_d_r___s_i_m_p_l_e_(_) here instead of _x_d_r___r_e_f_e_r_e_n_c_e(). 33.. LLoowweesstt LLaayyeerr ooff RRPPCC In the examples given so far, RPC takes care of many details automatically for you. In this section, we'll show you how you can change the defaults by using lower layers of the RPC library. It is assumed that you are familiar with sockets and the system calls for dealing with them. There are several occasions when you may need to use lower layers of RPC. First, you may need to use TCP, since the higher layer uses UDP, which restricts RPC calls to 8K bytes of data. Using TCP permits calls to send long streams of data. For an example, see the _T_C_P section below. Second, you may want to allocate and free memory while serializing or deserializing with XDR routines. There is no call at the higher level to let you free memory explicitly. For more explanation, see the _M_e_m_o_r_y _A_l_l_o_c_a_t_i_o_n _w_i_t_h _X_D_R section below. Third, you may need to perform authentication on either the client or server side, by supplying credentials or verifying them. See the explanation in the _A_u_t_h_e_n_t_i_c_a_- _t_i_o_n section below. Page 14 Remote Procedure Call Programming Guide 33..11.. MMoorree oonn tthhee SSeerrvveerr SSiiddee The server for the _n_u_s_e_r_s_(_) program shown below does the same thing as the one using _r_e_g_i_s_t_e_r_r_p_c_(_) above, but is written using a lower layer of the RPC package: Remote Procedure Call Programming Guide Page 15 ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> mmaaiinn(()) {{ SSVVCCXXPPRRTT **ttrraannsspp;; iinntt nnuusseerr(());; ttrraannsspp == ssvvccuuddpp__ccrreeaattee((RRPPCC__AANNYYSSOOCCKK));; iiff ((ttrraannsspp ==== NNUULLLL)){{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt ccrreeaattee aann RRPPCC sseerrvveerr\\nn""));; eexxiitt((11));; }} ppmmaapp__uunnsseett((RRUUSSEERRSSPPRROOGG,, RRUUSSEERRSSVVEERRSS));; iiff ((!!ssvvcc__rreeggiisstteerr((ttrraannsspp,, RRUUSSEERRSSPPRROOGG,, RRUUSSEERRSSVVEERRSS,, nnuusseerr,, IIPPPPRROOTTOO__UUDDPP)))) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeggiisstteerr RRUUSSEERR sseerrvviiccee\\nn""));; eexxiitt((11));; }} ssvvcc__rruunn(());; //** _N_e_v_e_r _r_e_t_u_r_n_s **// ffpprriinnttff((ssttddeerrrr,, ""sshhoouulldd nneevveerr rreeaacchh tthhiiss ppooiinntt\\nn""));; }} nnuusseerr((rrqqssttpp,, ttrraannsspp)) ssttrruucctt ssvvcc__rreeqq **rrqqssttpp;; SSVVCCXXPPRRTT **ttrraannsspp;; {{ uunnssiiggnneedd lloonngg nnuusseerrss;; sswwiittcchh ((rrqqssttpp-->>rrqq__pprroocc)) {{ ccaassee NNUULLLLPPRROOCC:: iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__vvooiidd,, 00)))) ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeppllyy ttoo RRPPCC ccaallll\\nn""));; rreettuurrnn;; ccaassee RRUUSSEERRSSPPRROOCC__NNUUMM:: _/_* _* _C_o_d_e _h_e_r_e _t_o _c_o_m_p_u_t_e _t_h_e _n_u_m_b_e_r _o_f _u_s_e_r_s _* _a_n_d _a_s_s_i_g_n _i_t _t_o _t_h_e _v_a_r_i_a_b_l_e _n_u_s_e_r_s _*_/ iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__uu__lloonngg,, &&nnuusseerrss)))) ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeppllyy ttoo RRPPCC ccaallll\\nn""));; rreettuurrnn;; ddeeffaauulltt:: ssvvcceerrrr__nnoopprroocc((ttrraannsspp));; rreettuurrnn;; }} }} First, the server gets a transport handle, which is used for receiving and replying to RPC messages. _r_e_g_i_s_t_e_r_r_p_c_(_) uses _s_v_c_u_d_p___c_r_e_a_t_e_(_) to get a UDP handle. If you require a more Page 16 Remote Procedure Call Programming Guide reliable protocol, call _s_v_c_t_c_p___c_r_e_a_t_e_(_) instead. If the argument to _s_v_c_u_d_p___c_r_e_a_t_e_(_) is _R_P_C___A_N_Y_S_O_C_K the RPC library creates a socket on which to receive and reply to RPC calls. Otherwise, _s_v_c_u_d_p___c_r_e_a_t_e_(_) expects its argument to be a valid socket number. If you specify your own socket, it can be bound or unbound. If it is bound to a port by the user, the port numbers of _s_v_c_u_d_p___c_r_e_a_t_e_(_) and _c_l_n_t_t_c_p___c_r_e_a_t_e_(_) (the low-level client routine) must match. If the user specifies the _R_P_C___A_N_Y_S_O_C_K argument, the RPC library routines will open sockets. Otherwise they will expect the user to do so. The routines _s_v_c_u_d_p___c_r_e_a_t_e_(_) and _c_l_n_t_u_d_p___c_r_e_a_t_e_(_) will cause the RPC library routines to _b_i_n_d_(_) their socket if it is not bound already. A service may choose to register its port number with the local portmapper service. This is done is done by specify- ing a non-zero protocol number in _s_v_c___r_e_g_i_s_t_e_r(). Inci- dently, a client can discover the server's port number by consulting the portmapper on their server's machine. This can be done automatically by specifying a zero port number in _c_l_n_t_u_d_p___c_r_e_a_t_e_(_) or _c_l_n_t_t_c_p___c_r_e_a_t_e(). After creating an _S_V_C_X_P_R_T, the next step is to call _p_m_a_p___u_n_s_e_t_(_) so that if the _n_u_s_e_r_s_(_) server crashed earlier, any previous trace of it is erased before restarting. More precisely, _p_m_a_p___u_n_s_e_t_(_) erases the entry for _R_U_S_E_R_S_P_R_O_G from the port mapper's tables. Finally, we associate the program number for _n_u_s_e_r_s_(_) with the procedure _n_u_s_e_r(). The final argument to _s_v_c___r_e_g_i_s_t_e_r_(_) is normally the protocol being used, which, in this case, is _I_P_P_R_O_T_O___U_D_P Notice that unlike _r_e_g_i_s_t_e_r_r_p_c(), there are no XDR routines involved in the registration process. Also, registration is done on the program, rather than procedure, level. The user routine _n_u_s_e_r_(_) must call and dispatch the appro- priate XDR routines based on the procedure number. Note that two things are handled by _n_u_s_e_r_(_) that _r_e_g_i_s_t_e_r_r_p_c_(_) handles automatically. The first is that procedure _N_U_L_L_P_R_O_C (currently zero) returns with no results. This can be used as a simple test for detecting if a remote program is run- ning. Second, there is a check for invalid procedure num- bers. If one is detected, _s_v_c_e_r_r___n_o_p_r_o_c_(_) is called to han- dle the error. Remote Procedure Call Programming Guide Page 17 The user service routine serializes the results and returns them to the RPC caller via _s_v_c___s_e_n_d_r_e_p_l_y_(_) Its first parame- ter is the _S_V_C_X_P_R_T handle, the second is the XDR routine, and the third is a pointer to the data to be returned. Not illustrated above is how a server handles an RPC program that receives data. As an example, we can add a procedure _R_U_S_E_R_S_P_R_O_C___B_O_O_L which has an argument _n_u_s_e_r_s(), and returns _T_R_U_E or _F_A_L_S_E depending on whether there are nusers logged on. It would look like this: ccaassee RRUUSSEERRSSPPRROOCC__BBOOOOLL:: {{ iinntt bbooooll;; uunnssiiggnneedd nnuusseerrqquueerryy;; iiff ((!!ssvvcc__ggeettaarrggss((ttrraannsspp,, xxddrr__uu__iinntt,, &&nnuusseerrqquueerryy)) {{ ssvvcceerrrr__ddeeccooddee((ttrraannsspp));; rreettuurrnn;; }} _/_* _* _C_o_d_e _t_o _s_e_t _n_u_s_e_r_s _= _n_u_m_b_e_r _o_f _u_s_e_r_s _*_/ iiff ((nnuusseerrqquueerryy ==== nnuusseerrss)) bbooooll == TTRRUUEE;; eellssee bbooooll == FFAALLSSEE;; iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__bbooooll,, &&bbooooll)))) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeppllyy ttoo RRPPCC ccaallll\\nn""));; rreettuurrnn ((11));; }} rreettuurrnn;; }} The relevant routine is _s_v_c___g_e_t_a_r_g_s_(_) which takes an _S_V_C_X_P_R_T handle, the XDR routine, and a pointer to where the input is to be placed as arguments. 33..22.. MMeemmoorryy AAllllooccaattiioonn wwiitthh XXDDRR XDR routines not only do input and output, they also do mem- ory allocation. This is why the second parameter of _x_d_r___a_r_r_a_y_(_) is a pointer to an array, rather than the array itself. If it is _N_U_L_L, then _x_d_r___a_r_r_a_y_(_) allocates space for the array and returns a pointer to it, putting the size of the array in the third argument. As an example, consider the following XDR routine _x_d_r___c_h_a_r_a_r_r_1_(_) which deals with a fixed array of bytes with length _S_I_Z_E. Page 18 Remote Procedure Call Programming Guide xxddrr__cchhaarraarrrr11((xxddrrsspp,, cchhaarraarrrr)) XXDDRR **xxddrrsspp;; cchhaarr cchhaarraarrrr[[]];; {{ cchhaarr **pp;; iinntt lleenn;; pp == cchhaarraarrrr;; lleenn == SSIIZZEE;; rreettuurrnn ((xxddrr__bbyytteess((xxddrrsspp,, &&pp,, &&lleenn,, SSIIZZEE))));; }} If space has already been allocated in _c_h_a_r_a_r_r, it can be called from a server like this: cchhaarr cchhaarraarrrr[[SSIIZZEE]];; ssvvcc__ggeettaarrggss((ttrraannsspp,, xxddrr__cchhaarraarrrr11,, cchhaarraarrrr));; If you want XDR to do the allocation, you would have to rewrite this routine in the following way: xxddrr__cchhaarraarrrr22((xxddrrsspp,, cchhaarraarrrrpp)) XXDDRR **xxddrrsspp;; cchhaarr ****cchhaarraarrrrpp;; {{ iinntt lleenn;; lleenn == SSIIZZEE;; rreettuurrnn ((xxddrr__bbyytteess((xxddrrsspp,, cchhaarrrraarrrrpp,, &&lleenn,, SSIIZZEE))));; }} Then the RPC call might look like this: cchhaarr **aarrrrppttrr;; aarrrrppttrr == NNUULLLL;; ssvvcc__ggeettaarrggss((ttrraannsspp,, xxddrr__cchhaarraarrrr22,, &&aarrrrppttrr));; _/_* _* _U_s_e _t_h_e _r_e_s_u_l_t _h_e_r_e _*_/ ssvvcc__ffrreeeeaarrggss((ttrraannsspp,, xxddrr__cchhaarraarrrr22,, &&aarrrrppttrr));; Note that, after being used, the character array can be freed with _s_v_c___f_r_e_e_a_r_g_s_(_) _s_v_c___f_r_e_e_a_r_g_s_(_) will not attempt to free any memory if the variable indicating it is NULL. For example, in the the routine _x_d_r___f_i_n_a_l_e_x_a_m_p_l_e(), given ear- lier, if _f_i_n_a_l_p_-_>_s_t_r_i_n_g was NULL, then it would not be freed. The same is true for _f_i_n_a_l_p_-_>_s_i_m_p_l_e_p. To summarize, each XDR routine is responsible for serializ- ing, deserializing, and freeing memory. When an XDR routine is called from _c_a_l_l_r_p_c_(_) the serializing part is used. When called from _s_v_c___g_e_t_a_r_g_s_(_) the deserializer is used. And Remote Procedure Call Programming Guide Page 19 when called from _s_v_c___f_r_e_e_a_r_g_s_(_) the memory deallocator is used. When building simple examples like those in this sec- tion, a user doesn't have to worry about the three modes. See the _E_x_t_e_r_n_a_l _D_a_t_a _R_e_p_r_e_s_e_n_t_a_t_i_o_n_: _S_u_n _T_e_c_h_n_i_c_a_l _N_o_t_e_s for examples of more sophisticated XDR routines that deter- mine which of the three modes they are in and adjust their behavior accordingly. Page 20 Remote Procedure Call Programming Guide 33..33.. TThhee CCaalllliinngg SSiiddee When you use _c_a_l_l_r_p_c_(_) you have no control over the RPC delivery mechanism or the socket used to transport the data. To illustrate the layer of RPC that lets you adjust these parameters, consider the following code to call the _n_u_s_e_r_s service: ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> mmaaiinn((aarrggcc,, aarrggvv)) iinntt aarrggcc;; cchhaarr ****aarrggvv;; {{ ssttrruucctt hhoosstteenntt **hhpp;; ssttrruucctt ttiimmeevvaall ppeerrttrryy__ttiimmeeoouutt,, ttoottaall__ttiimmeeoouutt;; ssttrruucctt ssoocckkaaddddrr__iinn sseerrvveerr__aaddddrr;; iinntt ssoocckk == RRPPCC__AANNYYSSOOCCKK;; rreeggiisstteerr CCLLIIEENNTT **cclliieenntt;; eennuumm ccllnntt__ssttaatt ccllnntt__ssttaatt;; uunnssiiggnneedd lloonngg nnuusseerrss;; iiff ((aarrggcc !!== 22)) {{ ffpprriinnttff((ssttddeerrrr,, ""uussaaggee:: nnuusseerrss hhoossttnnaammee\\nn""));; eexxiitt((--11));; }} iiff ((((hhpp == ggeetthhoossttbbyynnaammee((aarrggvv[[11]])))) ==== NNUULLLL)) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt ggeett aaddddrr ffoorr %%ss\\nn"",,aarrggvv[[11]]));; eexxiitt((--11));; }} ppeerrttrryy__ttiimmeeoouutt..ttvv__sseecc == 33;; ppeerrttrryy__ttiimmeeoouutt..ttvv__uusseecc == 00;; bbccooppyy((hhpp-->>hh__aaddddrr,, ((ccaaddddrr__tt))&&sseerrvveerr__aaddddrr..ssiinn__aaddddrr,, hhpp-->>hh__lleennggtthh));; sseerrvveerr__aaddddrr..ssiinn__ffaammiillyy == AAFF__IINNEETT;; sseerrvveerr__aaddddrr..ssiinn__ppoorrtt == 00;; iiff ((((cclliieenntt == ccllnnttuuddpp__ccrreeaattee((&&sseerrvveerr__aaddddrr,, RRUUSSEERRSSPPRROOGG,, RRUUSSEERRSSVVEERRSS,, ppeerrttrryy__ttiimmeeoouutt,, &&ssoocckk)))) ==== NNUULLLL)) {{ ccllnntt__ppccrreeaatteeeerrrroorr((""ccllnnttuuddpp__ccrreeaattee""));; eexxiitt((--11));; }} ttoottaall__ttiimmeeoouutt..ttvv__sseecc == 2200;; ttoottaall__ttiimmeeoouutt..ttvv__uusseecc == 00;; ccllnntt__ssttaatt == ccllnntt__ccaallll((cclliieenntt,, RRUUSSEERRSSPPRROOCC__NNUUMM,, xxddrr__vvooiidd,, 00,, xxddrr__uu__lloonngg,, &&nnuusseerrss,, ttoottaall__ttiimmeeoouutt));; iiff ((ccllnntt__ssttaatt !!== RRPPCC__SSUUCCCCEESSSS)) {{ ccllnntt__ppeerrrroorr((cclliieenntt,, ""rrppcc""));; eexxiitt((--11));; Remote Procedure Call Programming Guide Page 21 }} ccllnntt__ddeessttrrooyy((cclliieenntt));; cclloossee((ssoocckk));; eexxiitt((00));; }} The low-level version of _c_a_l_l_r_p_c_(_) is _c_l_n_t___c_a_l_l_(_) which takes a _C_L_I_E_N_T pointer rather than a host name. The parame- ters to _c_l_n_t___c_a_l_l_(_) are a _C_L_I_E_N_T pointer, the procedure num- ber, the XDR routine for serializing the argument, a pointer to the argument, the XDR routine for deserializing the return value, a pointer to where the return value will be placed, and the time in seconds to wait for a reply. The _C_L_I_E_N_T pointer is encoded with the transport mechanism. _c_a_l_l_r_p_c_(_) uses UDP, thus it calls _c_l_n_t_u_d_p___c_r_e_a_t_e_(_) to get a _C_L_I_E_N_T pointer. To get TCP (Transmission Control Protocol), you would use _c_l_n_t_t_c_p___c_r_e_a_t_e_(_). The parameters to _c_l_n_t_u_d_p___c_r_e_a_t_e_(_) are the server address, the program number, the version number, a timeout value (between tries), and a pointer to a socket. The final argu- ment to _c_l_n_t___c_a_l_l_(_) is the total time to wait for a response. Thus, the number of tries is the _c_l_n_t___c_a_l_l_(_) timeout divided by the _c_l_n_t_u_d_p___c_r_e_a_t_e_(_) timeout. Note that the _c_l_n_t___d_e_s_t_r_o_y_(_) call always deallocates the space associated with the _C_L_I_E_N_T handle. It closes the socket associated with the _C_L_I_E_N_T handle, however, only if the RPC library opened it. It the socket was opened by the user, it stays open. This makes it possible, in cases where there are multiple client handles using the same socket, to destroy one handle without closing the socket that other handles are using. To make a stream connection, the call to _c_l_n_t_u_d_p___c_r_e_a_t_e_(_) is replaced with a call to _c_l_n_t_t_c_p___c_r_e_a_t_e_(_). ccllnnttttccpp__ccrreeaattee((&&sseerrvveerr__aaddddrr,, pprrooggnnuumm,, vveerrssnnuumm,, &&ssoocckk,, iinnppuuttssiizzee,, oouuttppuuttssiizzee));; There is no timeout argument; instead, the receive and send buffer sizes must be specified. When the _c_l_n_t_t_c_p___c_r_e_a_t_e_(_) call is made, a TCP connection is established. All RPC calls using that _C_L_I_E_N_T handle would use this connection. The server side of an RPC call using TCP has _s_v_c_u_d_p___c_r_e_a_t_e_(_) replaced by _s_v_c_t_c_p___c_r_e_a_t_e_(_). ttrraannsspp == ssvvccttccpp__ccrreeaattee((RRPPCC__AANNYYSSOOCCKK,, 00,, 00));; The last two arguments to _s_v_c_t_c_p___c_r_e_a_t_e_(_) are send and receive sizes respectively. If `0' is specified for either of these, the system chooses a reasonable default. Page 22 Remote Procedure Call Programming Guide 44.. OOtthheerr RRPPCC FFeeaattuurreess This section discusses some other aspects of RPC that are occasionally useful. 44..11.. SSeelleecctt oonn tthhee SSeerrvveerr SSiiddee Suppose a process is processing RPC requests while perform- ing some other activity. If the other activity involves periodically updating a data structure, the process can set an alarm signal before calling _s_v_c___r_u_n_(_) But if the other activity involves waiting on a a file descriptor, the _s_v_c___r_u_n_(_) call won't work. The code for _s_v_c___r_u_n_(_) is as follows: vvooiidd ssvvcc__rruunn(()) {{ ffdd__sseett rreeaaddffddss;; iinntt ddttbbsszz == ggeettddttaabblleessiizzee(());; ffoorr ((;;;;)) {{ rreeaaddffddss == ssvvcc__ffddss;; sswwiittcchh ((sseelleecctt((ddttbbsszz,, &&rreeaaddffddss,, NNUULLLL,,NNUULLLL,,NNUULLLL)))) {{ ccaassee --11:: iiff ((eerrrrnnoo ==== EEIINNTTRR)) ccoonnttiinnuuee;; ppeerrrroorr((""sseelleecctt""));; rreettuurrnn;; ccaassee 00:: bbrreeaakk;; ddeeffaauulltt:: ssvvcc__ggeettrreeqqsseett((&&rreeaaddffddss));; }} }} }} You can bypass _s_v_c___r_u_n_(_) and call _s_v_c___g_e_t_r_e_q_s_e_t_(_) yourself. All you need to know are the file descriptors of the socket(s) associated with the programs you are waiting on. Thus you can have your own _s_e_l_e_c_t_(_) that waits on both the RPC socket, and your own descriptors. Note that _s_v_c___f_d_s_(_) is a bit mask of all the file descriptors that RPC is using for services. It can change everytime that _a_n_y RPC library routine is called, because descriptors are constantly being opened and closed, for example for TCP connections. 44..22.. BBrrooaaddccaasstt RRPPCC The _p_o_r_t_m_a_p_p_e_r is a daemon that converts RPC program numbers into DARPA protocol port numbers; see the _p_o_r_t_m_a_p man page. You can't do broadcast RPC without the portmapper. Here are Remote Procedure Call Programming Guide Page 23 the main differences between broadcast RPC and normal RPC calls: 1. Normal RPC expects one answer, whereas broadcast RPC expects many answers (one or more answer from each responding machine). 2. Broadcast RPC can only be supported by packet-oriented (connectionless) transport protocols like UPD/IP. 3. The implementation of broadcast RPC treats all unsuc- cessful responses as garbage by filtering them out. Thus, if there is a version mismatch between the broad- caster and a remote service, the user of broadcast RPC never knows. 4. All broadcast messages are sent to the portmap port. Thus, only services that register themselves with their portmapper are accessible via the broadcast RPC mecha- nism. 5. Broadcast requests are limited in size to the MTU (Max- imum Transfer Unit) of the local network. For Ether- net, the MTU is 1500 bytes. 44..22..11.. BBrrooaaddccaasstt RRPPCC SSyynnooppssiiss ##iinncclluuddee <> .. .. .. eennuumm ccllnntt__ssttaatt ccllnntt__ssttaatt;; .. .. .. ccllnntt__ssttaatt == ccllnntt__bbrrooaaddccaasstt((pprrooggnnuumm,, vveerrssnnuumm,, pprrooccnnuumm,, iinnpprroocc,, iinn,, oouuttpprroocc,, oouutt,, eeaacchhrreessuulltt)) uu__lloonngg pprrooggnnuumm;; //** _p_r_o_g_r_a_m _n_u_m_b_e_r **// uu__lloonngg vveerrssnnuumm;; //** _v_e_r_s_i_o_n _n_u_m_b_e_r **// uu__lloonngg pprrooccnnuumm;; //** _p_r_o_c_e_d_u_r_e _n_u_m_b_e_r **// xxddrrpprroocc__tt iinnpprroocc;; //** _x_d_r _r_o_u_t_i_n_e _f_o_r _a_r_g_s **// ccaaddddrr__tt iinn;; //** _p_o_i_n_t_e_r _t_o _a_r_g_s **// xxddrrpprroocc__tt oouuttpprroocc;; //** _x_d_r _r_o_u_t_i_n_e _f_o_r _r_e_s_u_l_t_s **// ccaaddddrr__tt oouutt;; //** _p_o_i_n_t_e_r _t_o _r_e_s_u_l_t_s **// bbooooll__tt ((**eeaacchhrreessuulltt))(());;//** _c_a_l_l _w_i_t_h _e_a_c_h _r_e_s_u_l_t _g_o_t_t_e_n **// The procedure _e_a_c_h_r_e_s_u_l_t_(_) is called each time a valid result is obtained. It returns a boolean that indicates whether or not the user wants more responses. bbooooll__tt ddoonnee;; .. .. .. ddoonnee == eeaacchhrreessuulltt((rreessuullttsspp,, rraaddddrr)) ccaaddddrr__tt rreessuullttsspp;; ssttrruucctt ssoocckkaaddddrr__iinn **rraaddddrr;; //** _A_d_d_r _o_f _r_e_s_p_o_n_d_i_n_g _m_a_c_h_i_n_e **// If _d_o_n_e is _T_R_U_E, then broadcasting stops and _c_l_n_t___b_r_o_a_d_c_a_s_t_(_) returns successfully. Otherwise, the Page 24 Remote Procedure Call Programming Guide routine waits for another response. The request is rebroad- cast after a few seconds of waiting. If no responses come back, the routine returns with _R_P_C___T_I_M_E_D_O_U_T. 44..33.. BBaattcchhiinngg The RPC architecture is designed so that clients send a call message, and wait for servers to reply that the call suc- ceeded. This implies that clients do not compute while servers are processing a call. This is inefficient if the client does not want or need an acknowledgement for every message sent. It is possible for clients to continue com- puting while waiting for a response, using RPC batch facili- ties. RPC messages can be placed in a "pipeline" of calls to a desired server; this is called batching. Batching assumes that: 1) each RPC call in the pipeline requires no response from the server, and the server does not send a response message; and 2) the pipeline of calls is transported on a reliable byte stream transport such as TCP/IP. Since the server does not respond to every call, the client can gener- ate new calls in parallel with the server executing previous calls. Furthermore, the TCP/IP implementation can buffer up many call messages, and send them to the server in one _w_r_i_t_e_(_) system call. This overlapped execution greatly decreases the interprocess communication overhead of the client and server processes, and the total elapsed time of a series of calls. Since the batched calls are buffered, the client should eventually do a nonbatched call in order to flush the pipeline. A contrived example of batching follows. Assume a string rendering service (like a window system) has two similar calls: one renders a string and returns void results, while the other renders a string and remains silent. The service (using the TCP/IP transport) may look like: Remote Procedure Call Programming Guide Page 25 ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> vvooiidd wwiinnddoowwddiissppaattcchh(());; mmaaiinn(()) {{ SSVVCCXXPPRRTT **ttrraannsspp;; ttrraannsspp == ssvvccttccpp__ccrreeaattee((RRPPCC__AANNYYSSOOCCKK,, 00,, 00));; iiff ((ttrraannsspp ==== NNUULLLL)){{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt ccrreeaattee aann RRPPCC sseerrvveerr\\nn""));; eexxiitt((11));; }} ppmmaapp__uunnsseett((WWIINNDDOOWWPPRROOGG,, WWIINNDDOOWWVVEERRSS));; iiff ((!!ssvvcc__rreeggiisstteerr((ttrraannsspp,, WWIINNDDOOWWPPRROOGG,, WWIINNDDOOWWVVEERRSS,, wwiinnddoowwddiissppaattcchh,, IIPPPPRROOTTOO__TTCCPP)))) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeggiisstteerr WWIINNDDOOWW sseerrvviiccee\\nn""));; eexxiitt((11));; }} ssvvcc__rruunn(());; //** _N_e_v_e_r _r_e_t_u_r_n_s **// ffpprriinnttff((ssttddeerrrr,, ""sshhoouulldd nneevveerr rreeaacchh tthhiiss ppooiinntt\\nn""));; }} vvooiidd wwiinnddoowwddiissppaattcchh((rrqqssttpp,, ttrraannsspp)) ssttrruucctt ssvvcc__rreeqq **rrqqssttpp;; SSVVCCXXPPRRTT **ttrraannsspp;; {{ cchhaarr **ss == NNUULLLL;; sswwiittcchh ((rrqqssttpp-->>rrqq__pprroocc)) {{ ccaassee NNUULLLLPPRROOCC:: iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__vvooiidd,, 00)))) ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeppllyy ttoo RRPPCC ccaallll\\nn""));; rreettuurrnn;; ccaassee RREENNDDEERRSSTTRRIINNGG:: iiff ((!!ssvvcc__ggeettaarrggss((ttrraannsspp,, xxddrr__wwrraappssttrriinngg,, &&ss)))) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt ddeeccooddee aarrgguummeennttss\\nn""));; _/_* _* _T_e_l_l _c_a_l_l_e_r _h_e _s_c_r_e_w_e_d _u_p _*_/ ssvvcceerrrr__ddeeccooddee((ttrraannsspp));; bbrreeaakk;; }} _/_* _* _C_o_d_e _h_e_r_e _t_o _r_e_n_d_e_r _t_h_e _s_t_r_i_n_g _s _*_/ iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__vvooiidd,, NNUULLLL)))) ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeppllyy ttoo RRPPCC ccaallll\\nn""));; bbrreeaakk;; ccaassee RREENNDDEERRSSTTRRIINNGG__BBAATTCCHHEEDD:: iiff ((!!ssvvcc__ggeettaarrggss((ttrraannsspp,, xxddrr__wwrraappssttrriinngg,, &&ss)))) {{ Page 26 Remote Procedure Call Programming Guide ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt ddeeccooddee aarrgguummeennttss\\nn""));; _/_* _* _W_e _a_r_e _s_i_l_e_n_t _i_n _t_h_e _f_a_c_e _o_f _p_r_o_t_o_c_o_l _e_r_r_o_r_s _*_/ bbrreeaakk;; }} _/_* _* _C_o_d_e _h_e_r_e _t_o _r_e_n_d_e_r _s_t_r_i_n_g _s_, _b_u_t _s_e_n_d _n_o _r_e_p_l_y_! _*_/ bbrreeaakk;; ddeeffaauulltt:: ssvvcceerrrr__nnoopprroocc((ttrraannsspp));; rreettuurrnn;; }} _/_* _* _N_o_w _f_r_e_e _s_t_r_i_n_g _a_l_l_o_c_a_t_e_d _w_h_i_l_e _d_e_c_o_d_i_n_g _a_r_g_u_m_e_n_t_s _*_/ ssvvcc__ffrreeeeaarrggss((ttrraannsspp,, xxddrr__wwrraappssttrriinngg,, &&ss));; }} Of course the service could have one procedure that takes the string and a boolean to indicate whether or not the pro- cedure should respond. In order for a client to take advantage of batching, the client must perform RPC calls on a TCP-based transport and the actual calls must have the following attributes: 1) the result's XDR routine must be zero _N_U_L_L), and 2) the RPC call's timeout must be zero. Remote Procedure Call Programming Guide Page 27 Here is an example of a client that uses batching to render a bunch of strings; the batching is flushed when the client gets a null string (EOF): ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> mmaaiinn((aarrggcc,, aarrggvv)) iinntt aarrggcc;; cchhaarr ****aarrggvv;; {{ ssttrruucctt hhoosstteenntt **hhpp;; ssttrruucctt ttiimmeevvaall ppeerrttrryy__ttiimmeeoouutt,, ttoottaall__ttiimmeeoouutt;; ssttrruucctt ssoocckkaaddddrr__iinn sseerrvveerr__aaddddrr;; iinntt ssoocckk == RRPPCC__AANNYYSSOOCCKK;; rreeggiisstteerr CCLLIIEENNTT **cclliieenntt;; eennuumm ccllnntt__ssttaatt ccllnntt__ssttaatt;; cchhaarr bbuuff[[11000000]],, **ss == bbuuff;; iiff ((((cclliieenntt == ccllnnttttccpp__ccrreeaattee((&&sseerrvveerr__aaddddrr,, WWIINNDDOOWWPPRROOGG,, WWIINNDDOOWWVVEERRSS,, &&ssoocckk,, 00,, 00)))) ==== NNUULLLL)) {{ ppeerrrroorr((""ccllnnttttccpp__ccrreeaattee""));; eexxiitt((--11));; }} ttoottaall__ttiimmeeoouutt..ttvv__sseecc == 00;; ttoottaall__ttiimmeeoouutt..ttvv__uusseecc == 00;; wwhhiillee ((ssccaannff((""%%ss"",, ss)) !!== EEOOFF)) {{ ccllnntt__ssttaatt == ccllnntt__ccaallll((cclliieenntt,, RREENNDDEERRSSTTRRIINNGG__BBAATTCCHHEEDD,, xxddrr__wwrraappssttrriinngg,, &&ss,, NNUULLLL,, NNUULLLL,, ttoottaall__ttiimmeeoouutt));; iiff ((ccllnntt__ssttaatt !!== RRPPCC__SSUUCCCCEESSSS)) {{ ccllnntt__ppeerrrroorr((cclliieenntt,, ""bbaattcchheedd rrppcc""));; eexxiitt((--11));; }} }} //** _N_o_w _f_l_u_s_h _t_h_e _p_i_p_e_l_i_n_e **// ttoottaall__ttiimmeeoouutt..ttvv__sseecc == 2200;; ccllnntt__ssttaatt == ccllnntt__ccaallll((cclliieenntt,, NNUULLLLPPRROOCC,, xxddrr__vvooiidd,, NNUULLLL,, xxddrr__vvooiidd,, NNUULLLL,, ttoottaall__ttiimmeeoouutt));; iiff ((ccllnntt__ssttaatt !!== RRPPCC__SSUUCCCCEESSSS)) {{ ccllnntt__ppeerrrroorr((cclliieenntt,, ""rrppcc""));; eexxiitt((--11));; }} ccllnntt__ddeessttrrooyy((cclliieenntt));; eexxiitt((00));; }} Since the server sends no message, the clients cannot be notified of any of the failures that may occur. Therefore, Page 28 Remote Procedure Call Programming Guide clients are on their own when it comes to handling errors. The above example was completed to render all of the (2000) lines in the file _/_e_t_c_/_t_e_r_m_c_a_p. The rendering service did nothing but throw the lines away. The example was run in the following four configurations: 1) machine to itself, regular RPC; 2) machine to itself, batched RPC; 3) machine to another, regular RPC; and 4) machine to another, batched RPC. The results are as follows: 1) 50 seconds; 2) 16 sec- onds; 3) 52 seconds; 4) 10 seconds. Running _f_s_c_a_n_f_(_) on _/_e_t_c_/_t_e_r_m_c_a_p only requires six seconds. These timings show the advantage of protocols that allow for overlapped execu- tion, though these protocols are often hard to design. 44..44.. AAuutthheennttiiccaattiioonn In the examples presented so far, the caller never identi- fied itself to the server, and the server never required an ID from the caller. Clearly, some network services, such as a network filesystem, require stronger security than what has been presented so far. In reality, every RPC call is authenticated by the RPC pack- age on the server, and similarly, the RPC client package generates and sends authentication parameters. Just as dif- ferent transports (TCP/IP or UDP/IP) can be used when creat- ing RPC clients and servers, different forms of authentica- tion can be associated with RPC clients; the default authen- tication type used as a default is type _n_o_n_e. The authentication subsystem of the RPC package is open ended. That is, numerous types of authentication are easy to support. 44..44..11.. UUNNIIXX AAuutthheennttiiccaattiioonn _T_h_e _C_l_i_e_n_t _S_i_d_e When a caller creates a new RPC client handle as in: ccllnntt == ccllnnttuuddpp__ccrreeaattee((aaddddrreessss,, pprrooggnnuumm,, vveerrssnnuumm,, wwaaiitt,, ssoocckkpp)) the appropriate transport instance defaults the associate authentication handle to be ccllnntt-->>ccll__aauutthh == aauutthhnnoonnee__ccrreeaattee(());; The RPC client can choose to use _U_N_I_X style authentication by setting _c_l_n_t_-_>_c_l___a_u_t_h after creating the RPC client han- dle: ccllnntt-->>ccll__aauutthh == aauutthhuunniixx__ccrreeaattee__ddeeffaauulltt(());; Remote Procedure Call Programming Guide Page 29 This causes each RPC call associated with _c_l_n_t to carry with it the following authentication credentials structure: _/_* _* _U_N_I_X _s_t_y_l_e _c_r_e_d_e_n_t_i_a_l_s_. _*_/ ssttrruucctt aauutthhuunniixx__ppaarrmmss {{ uu__lloonngg aauupp__ttiimmee;; //** _c_r_e_d_e_n_t_i_a_l_s _c_r_e_a_t_i_o_n _t_i_m_e **// cchhaarr **aauupp__mmaacchhnnaammee;; //** _h_o_s_t _n_a_m_e _w_h_e_r_e _c_l_i_e_n_t _i_s **// iinntt aauupp__uuiidd;; //** _c_l_i_e_n_t_'_s _U_N_I_X _e_f_f_e_c_t_i_v_e _u_i_d **// iinntt aauupp__ggiidd;; //** _c_l_i_e_n_t_'_s _c_u_r_r_e_n_t _g_r_o_u_p _i_d **// uu__iinntt aauupp__lleenn;; //** _e_l_e_m_e_n_t _l_e_n_g_t_h _o_f _a_u_p___g_i_d_s **// iinntt **aauupp__ggiiddss;; //** _a_r_r_a_y _o_f _g_r_o_u_p_s _u_s_e_r _i_s _i_n **// }};; These fields are set by _a_u_t_h_u_n_i_x___c_r_e_a_t_e___d_e_f_a_u_l_t_(_) by invok- ing the appropriate system calls. Since the RPC user cre- ated this new style of authentication, the user is responsi- ble for destroying it with: aauutthh__ddeessttrrooyy((ccllnntt-->>ccll__aauutthh));; This should be done in all cases, to conserve memory. _T_h_e _S_e_r_v_e_r _S_i_d_e Service implementors have a harder time dealing with authen- tication issues since the RPC package passes the service dispatch routine a request that has an arbitrary authentica- tion style associated with it. Consider the fields of a request handle passed to a service dispatch routine: _/_* _* _A_n _R_P_C _S_e_r_v_i_c_e _r_e_q_u_e_s_t _*_/ ssttrruucctt ssvvcc__rreeqq {{ uu__lloonngg rrqq__pprroogg;; //** _s_e_r_v_i_c_e _p_r_o_g_r_a_m _n_u_m_b_e_r **// uu__lloonngg rrqq__vveerrss;; //** _s_e_r_v_i_c_e _p_r_o_t_o_c_o_l _v_e_r_s _n_u_m **// uu__lloonngg rrqq__pprroocc;; //** _d_e_s_i_r_e_d _p_r_o_c_e_d_u_r_e _n_u_m_b_e_r **// ssttrruucctt ooppaaqquuee__aauutthh rrqq__ccrreedd;; //** _r_a_w _c_r_e_d_e_n_t_i_a_l_s _f_r_o_m _w_i_r_e **// ccaaddddrr__tt rrqq__ccllnnttccrreedd;; //** _c_r_e_d_e_n_t_i_a_l_s _(_r_e_a_d _o_n_l_y_) **// }};; The _r_q___c_r_e_d is mostly opaque, except for one field of inter- est: the style or flavor of authentication credentials: Page 30 Remote Procedure Call Programming Guide _/_* _* _A_u_t_h_e_n_t_i_c_a_t_i_o_n _i_n_f_o_. _M_o_s_t_l_y _o_p_a_q_u_e _t_o _t_h_e _p_r_o_g_r_a_m_m_e_r_. _*_/ ssttrruucctt ooppaaqquuee__aauutthh {{ eennuumm__tt ooaa__ffllaavvoorr;; //** _s_t_y_l_e _o_f _c_r_e_d_e_n_t_i_a_l_s **// ccaaddddrr__tt ooaa__bbaassee;; //** _a_d_d_r_e_s_s _o_f _m_o_r_e _a_u_t_h _s_t_u_f_f **// uu__iinntt ooaa__lleennggtthh;; //** _n_o_t _t_o _e_x_c_e_e_d _M_A_X___A_U_T_H___B_Y_T_E_S _*_/ _}_; The RPC package guarantees the following to the service dis- patch routine: 1. That the request's _r_q___c_r_e_d is well formed. Thus the service implementor may inspect the request's _r_q___c_r_e_d_._o_a___f_l_a_v_o_r to determine which style of authenti- cation the caller used. The service implementor may also wish to inspect the other fields of _r_q___c_r_e_d if the style is not one of the styles supported by the RPC package. 2. That the request's _r_q___c_l_n_t_c_r_e_d field is either _N_U_L_L or points to a well formed structure that corresponds to a supported style of authentication credentials. Remem- ber that only _u_n_i_x style is currently supported, so (currently) _r_q___c_l_n_t_c_r_e_d could be cast to a pointer to an _a_u_t_h_u_n_i_x___p_a_r_m_s structure. If _r_q___c_l_n_t_c_r_e_d is _N_U_L_L, the service implementor may wish to inspect the other (opaque) fields of _r_q___c_r_e_d in case the service knows about a new type of authentication that the RPC package does not know about. Our remote users service example can be extended so that it computes results for all users except UID 16: Remote Procedure Call Programming Guide Page 31 nnuusseerr((rrqqssttpp,, ttrraannsspp)) ssttrruucctt ssvvcc__rreeqq **rrqqssttpp;; SSVVCCXXPPRRTT **ttrraannsspp;; {{ ssttrruucctt aauutthhuunniixx__ppaarrmmss **uunniixx__ccrreedd;; iinntt uuiidd;; uunnssiiggnneedd lloonngg nnuusseerrss;; _/_* _* _w_e _d_o_n_'_t _c_a_r_e _a_b_o_u_t _a_u_t_h_e_n_t_i_c_a_t_i_o_n _f_o_r _n_u_l_l _p_r_o_c _*_/ iiff ((rrqqssttpp-->>rrqq__pprroocc ==== NNUULLLLPPRROOCC)) {{ iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__vvooiidd,, 00)))) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeppllyy ttoo RRPPCC ccaallll\\nn""));; rreettuurrnn ((11));; }} rreettuurrnn;; }} _/_* _* _n_o_w _g_e_t _t_h_e _u_i_d _*_/ sswwiittcchh ((rrqqssttpp-->>rrqq__ccrreedd..ooaa__ffllaavvoorr)) {{ ccaassee AAUUTTHH__UUNNIIXX:: uunniixx__ccrreedd == ((ssttrruucctt aauutthhuunniixx__ppaarrmmss **))rrqqssttpp-->>rrqq__ccllnnttccrreedd;; uuiidd == uunniixx__ccrreedd-->>aauupp__uuiidd;; bbrreeaakk;; ccaassee AAUUTTHH__NNUULLLL:: ddeeffaauulltt:: ssvvcceerrrr__wweeaakkaauutthh((ttrraannsspp));; rreettuurrnn;; }} sswwiittcchh ((rrqqssttpp-->>rrqq__pprroocc)) {{ ccaassee RRUUSSEERRSSPPRROOCC__NNUUMM:: _/_* _* _m_a_k_e _s_u_r_e _c_a_l_l_e_r _i_s _a_l_l_o_w_e_d _t_o _c_a_l_l _t_h_i_s _p_r_o_c _*_/ iiff ((uuiidd ==== 1166)) {{ ssvvcceerrrr__ssyysstteemmeerrrr((ttrraannsspp));; rreettuurrnn;; }} _/_* _* _C_o_d_e _h_e_r_e _t_o _c_o_m_p_u_t_e _t_h_e _n_u_m_b_e_r _o_f _u_s_e_r_s _* _a_n_d _a_s_s_i_g_n _i_t _t_o _t_h_e _v_a_r_i_a_b_l_e _n_u_s_e_r_s _*_/ iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__uu__lloonngg,, &&nnuusseerrss)))) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeppllyy ttoo RRPPCC ccaallll\\nn""));; rreettuurrnn ((11));; }} rreettuurrnn;; ddeeffaauulltt:: ssvvcceerrrr__nnoopprroocc((ttrraannsspp));; rreettuurrnn;; }} Page 32 Remote Procedure Call Programming Guide }} A few things should be noted here. First, it is customary not to check the authentication parameters associated with the _N_U_L_L_P_R_O_C (procedure number zero). Second, if the authentication parameter's type is not suitable for your service, you should call _s_v_c_e_r_r___w_e_a_k_a_u_t_h_(_). And finally, the service protocol itself should return status for access denied; in the case of our example, the protocol does not have such a status, so we call the service primitive _s_v_c_e_r_r___s_y_s_t_e_m_e_r_r_(_) instead. The last point underscores the relation between the RPC authentication package and the services; RPC deals only with _a_u_t_h_e_n_t_i_c_a_t_i_o_n and not with individual services' _a_c_c_e_s_s _c_o_n_- _t_r_o_l. The services themselves must implement their own access control policies and reflect these policies as return statuses in their protocols. 44..55.. DDEESS AAuutthheennttiiccaattiioonn UNIX authentication is quite easy to defeat. Instead of using _a_u_t_h_u_n_i_x___c_r_e_a_t_e___d_e_f_a_u_l_t(), one can call _a_u_t_h_u_- _n_i_x___c_r_e_a_t_e_(_) and then modify the RPC authentication handle it returns by filling in whatever user ID and hostname they wish the server to think they have. DES authentication is thus recommended for people who want more security than UNIX authentication offers. The details of the DES authentication protocol are compli- cated and are not explained here. See _R_e_m_o_t_e _P_r_o_c_e_d_u_r_e _C_a_l_l_s_: _P_r_o_t_o_c_o_l _S_p_e_c_i_f_i_c_a_t_i_o_n for the details. In order for DES authentication to work, the _k_e_y_- _s_e_r_v_(_8_c_) daemon must be running on both the server and client machines. The users on these machines need public keys assigned by the network administrator in the _p_u_b_- _l_i_c_k_e_y_(_5_) database. And, they need to have decrypted their secret keys using their login password. This automatically happens when one logs in using _l_o_g_i_n_(_1_), or can be done manually using _k_e_y_l_o_g_i_n_(_1_). The _N_e_t_w_o_r_k _S_e_r_- _v_i_c_e_s chapter explains more how to setup secure networking. _C_l_i_e_n_t _S_i_d_e If a client wishes to use DES authentication, it must set its authentication handle appropriately. Here is an exam- ple: cl->cl_auth = authdes_create(servername, 60, &server_addr, NULL); The first argument is the network name or "netname" of the Remote Procedure Call Programming Guide Page 33 owner of the server process. Typically, server processes are root processes and their netname can be derived using the following call: char servername[MAXNETNAMELEN]; host2netname(servername, rhostname, NULL); Here, _r_h_o_s_t_n_a_m_e is the hostname of the machine the server process is running on. _h_o_s_t_2_n_e_t_n_a_m_e_(_) fills in _s_e_r_v_e_r_n_a_m_e to contain this root process's netname. If the server pro- cess was run by a regular user, one could use the call _u_s_e_r_2_n_e_t_n_a_m_e_(_) instead. Here is an example for a server process with the same user ID as the client: char servername[MAXNETNAMELEN]; user2netname(servername, getuid(), NULL); The last argument to both of these calls, _u_s_e_r_2_n_e_t_n_a_m_e_(_) and _h_o_s_t_2_n_e_t_n_a_m_e(), is the name of the naming domain where the server is located. The _N_U_L_L used here means "use the local domain name." The second argument to _a_u_t_h_d_e_s___c_r_e_a_t_e_(_) is a lifetime for the credential. Here it is set to sixty seconds. What that means is that the credential will expire 60 seconds from now. If some mischievous user tries to reuse the creden- tial, the server RPC subsystem will recognize that it has expired and not grant any requests. If the same mischievous user tries to reuse the credential within the sixty second lifetime, he will still be rejected because the server RPC subsystem remembers which credentials it has already seen in the near past, and will not grant requests to duplicates. The third argument to _a_u_t_h_d_e_s___c_r_e_a_t_e_(_) is the address of the host to synchronize with. In order for DES authentication to work, the server and client must agree upon the time. Here we pass the address of the server itself, so the client and server will both be using the same time: the server's time. The argument can be _N_U_L_L, which means "don't bother synchronizing." You should only do this if you are sure the client and server are already synchronized. The final argument to _a_u_t_h_d_e_s___c_r_e_a_t_e_(_) is the address of a DES encryption key to use for encrypting timestamps and data. If this argument is _N_U_L_L, as it is in this example, a random key will be chosen. The client may find out the encryption key being used by consulting the _a_h___k_e_y field of the authentication handle. _S_e_r_v_e_r _S_i_d_e Page 34 Remote Procedure Call Programming Guide The server side is a lot simpler than the client side. Here is the previous example rewritten to use _A_U_T_H___D_E_S instead of _A_U_T_H___U_N_I_X: ##iinncclluuddee <> ##iinncclluuddee <> .. .. .. .. .. .. nnuusseerr((rrqqssttpp,, ttrraannsspp)) ssttrruucctt ssvvcc__rreeqq **rrqqssttpp;; SSVVCCXXPPRRTT **ttrraannsspp;; {{ ssttrruucctt aauutthhddeess__ccrreedd **ddeess__ccrreedd;; iinntt uuiidd;; iinntt ggiidd;; iinntt ggiiddlleenn;; iinntt ggiiddlliisstt[[1100]];; _/_* _* _w_e _d_o_n_'_t _c_a_r_e _a_b_o_u_t _a_u_t_h_e_n_t_i_c_a_t_i_o_n _f_o_r _n_u_l_l _p_r_o_c _*_/ iiff ((rrqqssttpp-->>rrqq__pprroocc ==== NNUULLLLPPRROOCC)) {{ //** _s_a_m_e _a_s _b_e_f_o_r_e **// }} _/_* _* _n_o_w _g_e_t _t_h_e _u_i_d _*_/ sswwiittcchh ((rrqqssttpp-->>rrqq__ccrreedd..ooaa__ffllaavvoorr)) {{ ccaassee AAUUTTHH__DDEESS:: ddeess__ccrreedd == ((ssttrruucctt aauutthhddeess__ccrreedd **)) rrqqssttpp-->>rrqq__ccllnnttccrreedd;; iiff ((!! nneettnnaammee22uusseerr((ddeess__ccrreedd-->>aaddcc__ffuullllnnaammee..nnaammee,, &&uuiidd,, &&ggiidd,, &&ggiiddlleenn,, ggiiddlliisstt)))) {{ ffpprriinnttff((ssttddeerrrr,, ""uunnkknnoowwnn uusseerr:: %%ss00,, ddeess__ccrreedd-->>aaddcc__ffuullllnnaammee..nnaammee));; ssvvcceerrrr__ssyysstteemmeerrrr((ttrraannsspp));; rreettuurrnn;; }} bbrreeaakk;; ccaassee AAUUTTHH__NNUULLLL:: ddeeffaauulltt:: ssvvcceerrrr__wweeaakkaauutthh((ttrraannsspp));; rreettuurrnn;; }} _/_* _* _T_h_e _r_e_s_t _i_s _t_h_e _s_a_m_e _a_s _b_e_f_o_r_e _*_/ Note the use of the routine _n_e_t_n_a_m_e_2_u_s_e_r(), the inverse of _u_s_e_r_2_n_e_t_n_a_m_e(): it takes a network ID and converts to a unix ID. _n_e_t_n_a_m_e_2_u_s_e_r() also supplies the group IDs which we Remote Procedure Call Programming Guide Page 35 don't use in this example, but which may be useful to other UNIX programs. 44..66.. UUssiinngg IInneettdd An RPC server can be started from _i_n_e_t_d The only difference from the usual code is that the service creation routine should be called in the following form: ttrraannsspp == ssvvccuuddpp__ccrreeaattee((00));; //** _F_o_r _U_D_P **// ttrraannsspp == ssvvccttccpp__ccrreeaattee((00,,00,,00));; //** _F_o_r _l_i_s_t_e_n_e_r _T_C_P _s_o_c_k_e_t_s **// ttrraannsspp == ssvvccffdd__ccrreeaattee((00,,00,,00));; //** _F_o_r _c_o_n_n_e_c_t_e_d _T_C_P _s_o_c_k_e_t_s **// since _i_n_e_t passes a socket as file descriptor 0. Also, _s_v_c___r_e_g_i_s_t_e_r_(_) should be called as ssvvcc__rreeggiisstteerr((ttrraannsspp,, PPRROOGGNNUUMM,, VVEERRSSNNUUMM,, sseerrvviiccee,, 00));; with the final flag as 0, since the program would already be registered by _i_n_e_t_d Remember that if you want to exit from the server process and return control to _i_n_e_t you need to explicitly exit, since _s_v_c___r_u_n_(_) never returns. The format of entries in _/_e_t_c_/_i_n_e_t_d_._c_o_n_f for RPC services is in one of the following two forms: pp__nnaammee//vveerrssiioonn ddggrraamm rrppcc//uuddpp wwaaiitt//nnoowwaaiitt uusseerr sseerrvveerr aarrggss pp__nnaammee//vveerrssiioonn ssttrreeaamm rrppcc//ttccpp wwaaiitt//nnoowwaaiitt uusseerr sseerrvveerr aarrggss where _p___n_a_m_e is the symbolic name of the program as it appears in _r_p_c_(_5_), _s_e_r_v_e_r is the program implementing the server, and _p_r_o_g_r_a_m and _v_e_r_s_i_o_n are the program and version numbers of the service. For more information, see _i_n_e_t_d_._c_o_n_f_(_5_). If the same program handles multiple versions, then the ver- sion number can be a range, as in this example: rrssttaattdd//11--22 ddggrraamm rrppcc//uuddpp wwaaiitt rroooott //uussrr//eettcc//rrppcc..rrssttaattdd 55.. MMoorree EExxaammpplleess 55..11.. VVeerrssiioonnss By convention, the first version number of program _P_R_O_G is _P_R_O_G_V_E_R_S___O_R_I_G and the most recent version is _P_R_O_G_V_E_R_S Sup- pose there is a new version of the _u_s_e_r program that returns an _u_n_s_i_g_n_e_d _s_h_o_r_t rather than a _l_o_n_g. If we name this ver- sion _R_U_S_E_R_S_V_E_R_S___S_H_O_R_T then a server that wants to support both versions would do a double register. Page 36 Remote Procedure Call Programming Guide iiff ((!!ssvvcc__rreeggiisstteerr((ttrraannsspp,, RRUUSSEERRSSPPRROOGG,, RRUUSSEERRSSVVEERRSS__OORRIIGG,, nnuusseerr,, IIPPPPRROOTTOO__TTCCPP)))) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeggiisstteerr RRUUSSEERR sseerrvviiccee\\nn""));; eexxiitt((11));; }} iiff ((!!ssvvcc__rreeggiisstteerr((ttrraannsspp,, RRUUSSEERRSSPPRROOGG,, RRUUSSEERRSSVVEERRSS__SSHHOORRTT,, nnuusseerr,, IIPPPPRROOTTOO__TTCCPP)))) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeggiisstteerr RRUUSSEERR sseerrvviiccee\\nn""));; eexxiitt((11));; }} Both versions can be handled by the same C procedure: nnuusseerr((rrqqssttpp,, ttrraannsspp)) ssttrruucctt ssvvcc__rreeqq **rrqqssttpp;; SSVVCCXXPPRRTT **ttrraannsspp;; {{ uunnssiiggnneedd lloonngg nnuusseerrss;; uunnssiiggnneedd sshhoorrtt nnuusseerrss22;; sswwiittcchh ((rrqqssttpp-->>rrqq__pprroocc)) {{ ccaassee NNUULLLLPPRROOCC:: iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__vvooiidd,, 00)))) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeppllyy ttoo RRPPCC ccaallll\\nn""));; rreettuurrnn ((11));; }} rreettuurrnn;; ccaassee RRUUSSEERRSSPPRROOCC__NNUUMM:: _/_* _* _C_o_d_e _h_e_r_e _t_o _c_o_m_p_u_t_e _t_h_e _n_u_m_b_e_r _o_f _u_s_e_r_s _* _a_n_d _a_s_s_i_g_n _i_t _t_o _t_h_e _v_a_r_i_a_b_l_e _n_u_s_e_r_s _*_/ nnuusseerrss22 == nnuusseerrss;; sswwiittcchh ((rrqqssttpp-->>rrqq__vveerrss)) {{ ccaassee RRUUSSEERRSSVVEERRSS__OORRIIGG:: iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__uu__lloonngg,, &&nnuusseerrss)))) {{ ffpprriinnttff((ssttddeerrrr,,""ccaann''tt rreeppllyy ttoo RRPPCC ccaallll\\nn""));; }} bbrreeaakk;; ccaassee RRUUSSEERRSSVVEERRSS__SSHHOORRTT:: iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__uu__sshhoorrtt,, &&nnuusseerrss22)))) {{ ffpprriinnttff((ssttddeerrrr,,""ccaann''tt rreeppllyy ttoo RRPPCC ccaallll\\nn""));; }} bbrreeaakk;; }} ddeeffaauulltt:: ssvvcceerrrr__nnoopprroocc((ttrraannsspp));; rreettuurrnn;; }} }} Remote Procedure Call Programming Guide Page 37 55..22.. TTCCPP Here is an example that is essentially _r_c_p_. The initiator of the RPC _s_n_d call takes its standard input and sends it to the server _r_c_v which prints it on standard output. The RPC call uses TCP. This also illustrates an XDR procedure that behaves differently on serialization than on deserializa- tion. _/_* _* _T_h_e _x_d_r _r_o_u_t_i_n_e_: _* _o_n _d_e_c_o_d_e_, _r_e_a_d _f_r_o_m _w_i_r_e_, _w_r_i_t_e _o_n_t_o _f_p _* _o_n _e_n_c_o_d_e_, _r_e_a_d _f_r_o_m _f_p_, _w_r_i_t_e _o_n_t_o _w_i_r_e _*_/ ##iinncclluuddee <> ##iinncclluuddee <> xxddrr__rrccpp((xxddrrss,, ffpp)) XXDDRR **xxddrrss;; FFIILLEE **ffpp;; {{ uunnssiiggnneedd lloonngg ssiizzee;; cchhaarr bbuuff[[BBUUFFSSIIZZ]],, **pp;; iiff ((xxddrrss-->>xx__oopp ==== XXDDRR__FFRREEEE))//** nnootthhiinngg ttoo ffrreeee **// rreettuurrnn 11;; wwhhiillee ((11)) {{ iiff ((xxddrrss-->>xx__oopp ==== XXDDRR__EENNCCOODDEE)) {{ iiff ((((ssiizzee == ffrreeaadd((bbuuff,, ssiizzeeooff((cchhaarr)),, BBUUFFSSIIZZ,, ffpp)))) ==== 00 &&&& ffeerrrroorr((ffpp)))) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt ffrreeaadd\\nn""));; rreettuurrnn ((11));; }} }} pp == bbuuff;; iiff ((!!xxddrr__bbyytteess((xxddrrss,, &&pp,, &&ssiizzee,, BBUUFFSSIIZZ)))) rreettuurrnn 00;; iiff ((ssiizzee ==== 00)) rreettuurrnn 11;; iiff ((xxddrrss-->>xx__oopp ==== XXDDRR__DDEECCOODDEE)) {{ iiff ((ffwwrriittee((bbuuff,, ssiizzeeooff((cchhaarr)),, ssiizzee,, ffpp)) !!== ssiizzee)) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt ffwwrriittee\\nn""));; rreettuurrnn ((11));; }} }} }} }} Page 38 Remote Procedure Call Programming Guide _/_* _* _T_h_e _s_e_n_d_e_r _r_o_u_t_i_n_e_s _*_/ ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> mmaaiinn((aarrggcc,, aarrggvv)) iinntt aarrggcc;; cchhaarr ****aarrggvv;; {{ iinntt xxddrr__rrccpp(());; iinntt eerrrr;; iiff ((aarrggcc << 22)) {{ ffpprriinnttff((ssttddeerrrr,, ""uussaaggee:: %%ss sseerrvveerrnnaammee\\nn"",, aarrggvv[[00]]));; eexxiitt((--11));; }} iiff ((((eerrrr == ccaallllrrppccttccpp((aarrggvv[[11]],, RRCCPPPPRROOGG,, RRCCPPPPRROOCC,, RRCCPPVVEERRSS,, xxddrr__rrccpp,, ssttddiinn,, xxddrr__vvooiidd,, 00)) !!== 00)))) {{ ccllnntt__ppeerrrrnnoo((eerrrr));; ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt mmaakkee RRPPCC ccaallll\\nn""));; eexxiitt((11));; }} eexxiitt((00));; }} ccaallllrrppccttccpp((hhoosstt,, pprrooggnnuumm,, pprrooccnnuumm,, vveerrssnnuumm,, iinnpprroocc,, iinn,, oouuttpprroocc,, oouutt)) cchhaarr **hhoosstt,, **iinn,, **oouutt;; xxddrrpprroocc__tt iinnpprroocc,, oouuttpprroocc;; {{ ssttrruucctt ssoocckkaaddddrr__iinn sseerrvveerr__aaddddrr;; iinntt ssoocckkeett == RRPPCC__AANNYYSSOOCCKK;; eennuumm ccllnntt__ssttaatt ccllnntt__ssttaatt;; ssttrruucctt hhoosstteenntt **hhpp;; rreeggiisstteerr CCLLIIEENNTT **cclliieenntt;; ssttrruucctt ttiimmeevvaall ttoottaall__ttiimmeeoouutt;; iiff ((((hhpp == ggeetthhoossttbbyynnaammee((hhoosstt)))) ==== NNUULLLL)) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt ggeett aaddddrr ffoorr ''%%ss''\\nn"",, hhoosstt));; rreettuurrnn ((--11));; }} bbccooppyy((hhpp-->>hh__aaddddrr,, ((ccaaddddrr__tt))&&sseerrvveerr__aaddddrr..ssiinn__aaddddrr,, hhpp-->>hh__lleennggtthh));; sseerrvveerr__aaddddrr..ssiinn__ffaammiillyy == AAFF__IINNEETT;; sseerrvveerr__aaddddrr..ssiinn__ppoorrtt == 00;; iiff ((((cclliieenntt == ccllnnttttccpp__ccrreeaattee((&&sseerrvveerr__aaddddrr,, pprrooggnnuumm,, vveerrssnnuumm,, &&ssoocckkeett,, BBUUFFSSIIZZ,, BBUUFFSSIIZZ)))) ==== NNUULLLL)) {{ ppeerrrroorr((""rrppccttccpp__ccrreeaattee""));; rreettuurrnn ((--11));; }} Remote Procedure Call Programming Guide Page 39 ttoottaall__ttiimmeeoouutt..ttvv__sseecc == 2200;; ttoottaall__ttiimmeeoouutt..ttvv__uusseecc == 00;; ccllnntt__ssttaatt == ccllnntt__ccaallll((cclliieenntt,, pprrooccnnuumm,, iinnpprroocc,, iinn,, oouuttpprroocc,, oouutt,, ttoottaall__ttiimmeeoouutt));; ccllnntt__ddeessttrrooyy((cclliieenntt));; rreettuurrnn ((iinntt))ccllnntt__ssttaatt;; }} Page 40 Remote Procedure Call Programming Guide _/_* _* _T_h_e _r_e_c_e_i_v_i_n_g _r_o_u_t_i_n_e_s _*_/ ##iinncclluuddee <> ##iinncclluuddee <> mmaaiinn(()) {{ rreeggiisstteerr SSVVCCXXPPRRTT **ttrraannsspp;; iinntt rrccpp__sseerrvviiccee(()),, xxddrr__rrccpp(());; iiff ((((ttrraannsspp == ssvvccttccpp__ccrreeaattee((RRPPCC__AANNYYSSOOCCKK,, BBUUFFSSIIZZ,, BBUUFFSSIIZZ)))) ==== NNUULLLL)) {{ ffpprriinnttff((""ssvvccttccpp__ccrreeaattee:: eerrrroorr\\nn""));; eexxiitt((11));; }} ppmmaapp__uunnsseett((RRCCPPPPRROOGG,, RRCCPPVVEERRSS));; iiff ((!!ssvvcc__rreeggiisstteerr((ttrraannsspp,, RRCCPPPPRROOGG,, RRCCPPVVEERRSS,, rrccpp__sseerrvviiccee,, IIPPPPRROOTTOO__TTCCPP)))) {{ ffpprriinnttff((ssttddeerrrr,, ""ssvvcc__rreeggiisstteerr:: eerrrroorr\\nn""));; eexxiitt((11));; }} ssvvcc__rruunn(());; //** _n_e_v_e_r _r_e_t_u_r_n_s **// ffpprriinnttff((ssttddeerrrr,, ""ssvvcc__rruunn sshhoouulldd nneevveerr rreettuurrnn\\nn""));; }} rrccpp__sseerrvviiccee((rrqqssttpp,, ttrraannsspp)) rreeggiisstteerr ssttrruucctt ssvvcc__rreeqq **rrqqssttpp;; rreeggiisstteerr SSVVCCXXPPRRTT **ttrraannsspp;; {{ sswwiittcchh ((rrqqssttpp-->>rrqq__pprroocc)) {{ ccaassee NNUULLLLPPRROOCC:: iiff ((ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__vvooiidd,, 00)) ==== 00)) {{ ffpprriinnttff((ssttddeerrrr,, ""eerrrr:: rrccpp__sseerrvviiccee""));; rreettuurrnn ((11));; }} rreettuurrnn;; ccaassee RRCCPPPPRROOCC__FFPP:: iiff ((!!ssvvcc__ggeettaarrggss((ttrraannsspp,, xxddrr__rrccpp,, ssttddoouutt)))) {{ ssvvcceerrrr__ddeeccooddee((ttrraannsspp));; rreettuurrnn;; }} iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__vvooiidd,, 00)))) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaann''tt rreeppllyy\\nn""));; rreettuurrnn;; }} rreettuurrnn ((00));; ddeeffaauulltt:: ssvvcceerrrr__nnoopprroocc((ttrraannsspp));; rreettuurrnn;; }} }} Remote Procedure Call Programming Guide Page 41 55..33.. CCaallllbbaacckk PPrroocceedduurreess Occasionally, it is useful to have a server become a client, and make an RPC call back to the process which is its client. An example is remote debugging, where the client is a window system program, and the server is a debugger run- ning on the remote machine. Most of the time, the user clicks a mouse button at the debugging window, which con- verts this to a debugger command, and then makes an RPC call to the server (where the debugger is actually running), telling it to execute that command. However, when the debugger hits a breakpoint, the roles are reversed, and the debugger wants to make an rpc call to the window program, so that it can inform the user that a breakpoint has been reached. In order to do an RPC callback, you need a program number to make the RPC call on. Since this will be a dynamically gen- erated program number, it should be in the transient range, _0_x_4_0_0_0_0_0_0_0 _- _0_x_5_f_f_f_f_f_f_f. The routine _g_e_t_t_r_a_n_s_i_e_n_t_(_) returns a valid program number in the transient range, and registers it with the portmapper. It only talks to the portmapper running on the same machine as the _g_e_t_t_r_a_n_s_i_e_n_t_(_) routine itself. The call to _p_m_a_p___s_e_t_(_) is a test and set operation, in that it indivisibly tests whether a program number has already been registered, and if it has not, then reserves it. On return, the _s_o_c_k_p argument will contain a socket that can be used as the argument to an _s_v_c_u_d_p___c_r_e_a_t_e_(_) or _s_v_c_t_c_p___c_r_e_a_t_e_(_) call. Page 42 Remote Procedure Call Programming Guide ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> ggeettttrraannssiieenntt((pprroottoo,, vveerrss,, ssoocckkpp)) iinntt pprroottoo,, vveerrss,, **ssoocckkpp;; {{ ssttaattiicc iinntt pprrooggnnuumm == 00xx4400000000000000;; iinntt ss,, lleenn,, ssoocckkttyyppee;; ssttrruucctt ssoocckkaaddddrr__iinn aaddddrr;; sswwiittcchh((pprroottoo)) {{ ccaassee IIPPPPRROOTTOO__UUDDPP:: ssoocckkttyyppee == SSOOCCKK__DDGGRRAAMM;; bbrreeaakk;; ccaassee IIPPPPRROOTTOO__TTCCPP:: ssoocckkttyyppee == SSOOCCKK__SSTTRREEAAMM;; bbrreeaakk;; ddeeffaauulltt:: ffpprriinnttff((ssttddeerrrr,, ""uunnkknnoowwnn pprroottooccooll ttyyppee\\nn""));; rreettuurrnn 00;; }} iiff ((**ssoocckkpp ==== RRPPCC__AANNYYSSOOCCKK)) {{ iiff ((((ss == ssoocckkeett((AAFF__IINNEETT,, ssoocckkttyyppee,, 00)))) << 00)) {{ ppeerrrroorr((""ssoocckkeett""));; rreettuurrnn ((00));; }} **ssoocckkpp == ss;; }} eellssee ss == **ssoocckkpp;; aaddddrr..ssiinn__aaddddrr..ss__aaddddrr == 00;; aaddddrr..ssiinn__ffaammiillyy == AAFF__IINNEETT;; aaddddrr..ssiinn__ppoorrtt == 00;; lleenn == ssiizzeeooff((aaddddrr));; _/_* _* _m_a_y _b_e _a_l_r_e_a_d_y _b_o_u_n_d_, _s_o _d_o_n_'_t _c_h_e_c_k _f_o_r _e_r_r_o_r _*_/ bbiinndd((ss,, &&aaddddrr,, lleenn));; iiff ((ggeettssoocckknnaammee((ss,, &&aaddddrr,, &&lleenn))<< 00)) {{ ppeerrrroorr((""ggeettssoocckknnaammee""));; rreettuurrnn ((00));; }} wwhhiillee ((!!ppmmaapp__sseett((pprrooggnnuumm++++,, vveerrss,, pprroottoo,, nnttoohhss((aaddddrr..ssiinn__ppoorrtt)))))) ccoonnttiinnuuee;; rreettuurrnn ((pprrooggnnuumm--11));; }} NNoottee:: _T_h_e _c_a_l_l _t_o _n_t_o_h_s_(_) _i_s _n_e_c_e_s_s_a_r_y _t_o _e_n_s_u_r_e _t_h_a_t _t_h_e _p_o_r_t _n_u_m_b_e_r _i_n _a_d_d_r_._s_i_n___p_o_r_t_, _w_h_i_c_h _i_s _i_n _n_e_t_w_o_r_k _b_y_t_e _o_r_d_e_r_, _i_s _p_a_s_s_e_d _i_n _h_o_s_t _b_y_t_e _o_r_d_e_r _(_a_s _p_m_a_p___s_e_t_(_) _e_x_p_e_c_t_s_)_. _S_e_e _t_h_e _b_y_t_e_o_r_d_e_r_(_3_N_) _m_a_n _p_a_g_e _f_o_r _m_o_r_e _d_e_t_a_i_l_s _o_n _t_h_e _c_o_n_- _v_e_r_s_i_o_n _o_f _n_e_t_w_o_r_k _a_d_d_r_e_s_s_e_s _f_r_o_m _n_e_t_w_o_r_k _t_o _h_o_s_t _b_y_t_e Remote Procedure Call Programming Guide Page 43 _o_r_d_e_r_. Page 44 Remote Procedure Call Programming Guide The following pair of programs illustrate how to use the _g_e_t_t_r_a_n_s_i_e_n_t_(_) routine. The client makes an RPC call to the server, passing it a transient program number. Then the client waits around to receive a callback from the server at that program number. The server registers the program _E_X_A_M_- _P_L_E_P_R_O_G so that it can receive the RPC call informing it of the callback program number. Then at some random time (on receiving an _A_L_R_M signal in this example), it sends a call- back RPC call, using the program number it received earlier. _/_* _* _c_l_i_e_n_t _*_/ ##iinncclluuddee <> ##iinncclluuddee <> iinntt ccaallllbbaacckk(());; cchhaarr hhoossttnnaammee[[225566]];; mmaaiinn(()) {{ iinntt xx,, aannss,, ss;; SSVVCCXXPPRRTT **xxpprrtt;; ggeetthhoossttnnaammee((hhoossttnnaammee,, ssiizzeeooff((hhoossttnnaammee))));; ss == RRPPCC__AANNYYSSOOCCKK;; xx == ggeettttrraannssiieenntt((IIPPPPRROOTTOO__UUDDPP,, 11,, &&ss));; ffpprriinnttff((ssttddeerrrr,, ""cclliieenntt ggeettss pprrooggnnuumm %%dd\\nn"",, xx));; iiff ((((xxpprrtt == ssvvccuuddpp__ccrreeaattee((ss)))) ==== NNUULLLL)) {{ ffpprriinnttff((ssttddeerrrr,, ""rrppcc__sseerrvveerr:: ssvvccuuddpp__ccrreeaattee\\nn""));; eexxiitt((11));; }} _/_* _p_r_o_t_o_c_o_l _i_s _0 _- _g_e_t_t_r_a_n_s_i_e_n_t _d_o_e_s _r_e_g_i_s_t_e_r_i_n_g _*_/ ((vvooiidd))ssvvcc__rreeggiisstteerr((xxpprrtt,, xx,, 11,, ccaallllbbaacckk,, 00));; aannss == ccaallllrrppcc((hhoossttnnaammee,, EEXXAAMMPPLLEEPPRROOGG,, EEXXAAMMPPLLEEVVEERRSS,, EEXXAAMMPPLLEEPPRROOCC__CCAALLLLBBAACCKK,, xxddrr__iinntt,, &&xx,, xxddrr__vvooiidd,, 00));; iiff ((((eennuumm ccllnntt__ssttaatt)) aannss !!== RRPPCC__SSUUCCCCEESSSS)) {{ ffpprriinnttff((ssttddeerrrr,, ""ccaallll:: ""));; ccllnntt__ppeerrrrnnoo((aannss));; ffpprriinnttff((ssttddeerrrr,, ""\\nn""));; }} ssvvcc__rruunn(());; ffpprriinnttff((ssttddeerrrr,, ""EErrrroorr:: ssvvcc__rruunn sshhoouullddnn''tt rreettuurrnn\\nn""));; }} ccaallllbbaacckk((rrqqssttpp,, ttrraannsspp)) rreeggiisstteerr ssttrruucctt ssvvcc__rreeqq **rrqqssttpp;; rreeggiisstteerr SSVVCCXXPPRRTT **ttrraannsspp;; {{ sswwiittcchh ((rrqqssttpp-->>rrqq__pprroocc)) {{ ccaassee 00:: iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__vvooiidd,, 00)))) {{ ffpprriinnttff((ssttddeerrrr,, ""eerrrr:: eexxaammpplleepprroogg\\nn""));; Remote Procedure Call Programming Guide Page 45 rreettuurrnn ((11));; }} rreettuurrnn ((00));; ccaassee 11:: iiff ((!!ssvvcc__ggeettaarrggss((ttrraannsspp,, xxddrr__vvooiidd,, 00)))) {{ ssvvcceerrrr__ddeeccooddee((ttrraannsspp));; rreettuurrnn ((11));; }} ffpprriinnttff((ssttddeerrrr,, ""cclliieenntt ggoott ccaallllbbaacckk\\nn""));; iiff ((!!ssvvcc__sseennddrreeppllyy((ttrraannsspp,, xxddrr__vvooiidd,, 00)))) {{ ffpprriinnttff((ssttddeerrrr,, ""eerrrr:: eexxaammpplleepprroogg""));; rreettuurrnn ((11));; }} }} }} Page 46 Remote Procedure Call Programming Guide _/_* _* _s_e_r_v_e_r _*_/ ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee <> cchhaarr **ggeettnneewwpprroogg(());; cchhaarr hhoossttnnaammee[[225566]];; iinntt ddooccaallllbbaacckk(());; iinntt ppnnuumm;; //** _p_r_o_g_r_a_m _n_u_m_b_e_r _f_o_r _c_a_l_l_b_a_c_k _r_o_u_t_i_n_e **// mmaaiinn(()) {{ ggeetthhoossttnnaammee((hhoossttnnaammee,, ssiizzeeooff((hhoossttnnaammee))));; rreeggiisstteerrrrppcc((EEXXAAMMPPLLEEPPRROOGG,, EEXXAAMMPPLLEEVVEERRSS,, EEXXAAMMPPLLEEPPRROOCC__CCAALLLLBBAACCKK,, ggeettnneewwpprroogg,, xxddrr__iinntt,, xxddrr__vvooiidd));; ffpprriinnttff((ssttddeerrrr,, ""sseerrvveerr ggooiinngg iinnttoo ssvvcc__rruunn\\nn""));; ssiiggnnaall((SSIIGGAALLRRMM,, ddooccaallllbbaacckk));; aallaarrmm((1100));; ssvvcc__rruunn(());; ffpprriinnttff((ssttddeerrrr,, ""EErrrroorr:: ssvvcc__rruunn sshhoouullddnn''tt rreettuurrnn\\nn""));; }} cchhaarr ** ggeettnneewwpprroogg((ppnnuummpp)) cchhaarr **ppnnuummpp;; {{ ppnnuumm == **((iinntt **))ppnnuummpp;; rreettuurrnn NNUULLLL;; }} ddooccaallllbbaacckk(()) {{ iinntt aannss;; aannss == ccaallllrrppcc((hhoossttnnaammee,, ppnnuumm,, 11,, 11,, xxddrr__vvooiidd,, 00,, xxddrr__vvooiidd,, 00));; iiff ((aannss !!== 00)) {{ ffpprriinnttff((ssttddeerrrr,, ""sseerrvveerr:: ""));; ccllnntt__ppeerrrrnnoo((aannss));; ffpprriinnttff((ssttddeerrrr,, ""\\nn""));; }} }}