BSD 4_3_Tahoe release
[unix-history] / usr / src / new / sunrpc / clnt_udp.c
CommitLineData
95f51977
C
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
30static char sccsid[] = "@(#)clnt_udp.c 1.5 85/03/17 Copyr 1984 Sun Micro";
31#endif
32
33/*
34 * clnt_udp.c, Implements a UPD/IP based, client side RPC.
35 *
36 * Copyright (C) 1984, Sun Microsystems, Inc.
37 */
38
39#include <stdio.h>
40#include "types.h"
41#include <sys/socket.h>
42#include <sys/time.h>
43#include <netinet/in.h>
44#include <netdb.h>
45#include <errno.h>
46#include "xdr.h"
47#include "auth.h"
48#include "clnt.h"
49#include "rpc_msg.h"
50#include "pmap_clnt.h"
51
52char *malloc();
53extern int errno;
54long random();
55
56/*
57 * UDP bases client side rpc operations
58 */
59static enum clnt_stat clntudp_call();
60static void clntudp_abort();
61static void clntudp_geterr();
62static bool_t clntudp_freeres();
63static void clntudp_destroy();
64
65static struct clnt_ops udp_ops = {
66 clntudp_call,
67 clntudp_abort,
68 clntudp_geterr,
69 clntudp_freeres,
70 clntudp_destroy
71};
72
73/*
74 * Private data kept per client handle
75 */
76struct cu_data {
77 int cu_sock;
78 struct sockaddr_in cu_raddr;
79 int cu_rlen;
80 struct timeval cu_wait;
81 struct rpc_err cu_error;
82 XDR cu_outxdrs;
83 u_int cu_xdrpos;
84 char cu_outbuf[UDPMSGSIZE];
85 char cu_inbuf[UDPMSGSIZE];
86};
87
88/*
89 * Create a UDP based client handle.
90 * If *sockp<0, *sockp is set to a newly created UPD socket.
91 * If raddr->sin_port is 0 a binder on the remote machine
92 * is consulted for the correct port number.
93 * NB: It is the clients responsibility to close *sockp.
94 * NB: The rpch->cl_auth is initialized to null authentication.
95 * Caller may wish to set this something more useful.
96 *
97 * wait is the amount of time used between retransmitting a call if
98 * no response has been heard; retransmition occurs until the actual
99 * rpc call times out.
100 */
101CLIENT *
102clntudp_create(raddr, program, version, wait, sockp)
103 struct sockaddr_in *raddr;
104 u_long program;
105 u_long version;
106 struct timeval wait;
107 register int *sockp;
108{
109 CLIENT *cl;
110 register struct cu_data *cu;
111 struct timeval now;
112 struct rpc_msg call_msg;
113
114 cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
115 if (cl == NULL) {
116 fprintf(stderr, "clntudp_create: out of memory\n");
117 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
118 rpc_createerr.cf_error.re_errno = errno;
119 goto fooy;
120 }
121 cu = (struct cu_data *)mem_alloc(sizeof(*cu));
122 if (cu == NULL) {
123 fprintf(stderr, "clntudp_create: out of memory\n");
124 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
125 rpc_createerr.cf_error.re_errno = errno;
126 goto fooy;
127 }
128
129 (void)gettimeofday(&now, (struct timezone *)0);
130 if (raddr->sin_port == 0) {
131 u_short port;
132 if ((port =
133 pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
134 goto fooy;
135 }
136 raddr->sin_port = htons(port);
137 }
138 cl->cl_ops = &udp_ops;
139 cl->cl_private = (caddr_t)cu;
140 cu->cu_raddr = *raddr;
141 cu->cu_rlen = sizeof (cu->cu_raddr);
142 cu->cu_wait = wait;
143 call_msg.rm_xid = getpid() ^ (int)random() ^ now.tv_sec ^ now.tv_usec;
144 call_msg.rm_direction = CALL;
145 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
146 call_msg.rm_call.cb_prog = program;
147 call_msg.rm_call.cb_vers = version;
148 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
149 UDPMSGSIZE, XDR_ENCODE);
150 if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
151 goto fooy;
152 }
153 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
154 cu->cu_sock = (*sockp < 0) ?
155 (*sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) :
156 *sockp;
157 if (cu->cu_sock < 0) {
158 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
159 rpc_createerr.cf_error.re_errno = errno;
160 goto fooy;
161 }
162 cl->cl_auth = authnone_create();
163 return (cl);
164fooy:
165 mem_free((caddr_t)cu, sizeof(*cu));
166 mem_free((caddr_t)cl, sizeof(CLIENT));
167 return ((CLIENT *)NULL);
168}
169
170static enum clnt_stat
171clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, timeout)
172 register CLIENT *cl; /* client handle */
173 u_long proc; /* procedure number */
174 xdrproc_t xargs; /* xdr routine for args */
175 caddr_t argsp; /* pointer to args */
176 xdrproc_t xresults; /* xdr routine for results */
177 caddr_t resultsp; /* pointer to results */
178 struct timeval timeout; /* seconds to wait before giving up */
179{
180 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
181 register XDR *xdrs;
182 register int outlen;
183 register int inlen;
184 int readfds, fromlen;
185 register int mask;
186 struct sockaddr_in from;
187 struct rpc_msg reply_msg;
188 XDR reply_xdrs;
189 struct timeval time_waited;
190 bool_t ok;
191
192call_again:
193 time_waited.tv_sec = 0;
194 time_waited.tv_usec = 0;
195 xdrs = &(cu->cu_outxdrs);
196 xdrs->x_op = XDR_ENCODE;
197 XDR_SETPOS(xdrs, cu->cu_xdrpos);
198 /*
199 * the transaction is the first thing in the out buffer
200 */
201 (*(u_short *)(cu->cu_outbuf))++;
202 if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
203 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
204 (! (*xargs)(xdrs, argsp)))
205 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
206 outlen = (int)XDR_GETPOS(xdrs);
207 while (TRUE) {
208
209 if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
210 (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen)
211 != outlen) {
212 cu->cu_error.re_errno = errno;
213 return (cu->cu_error.re_status = RPC_CANTSEND);
214 }
215 /*
216 * sub-optimal code appears inside the loop because we have
217 * some clock time to spare while the packets are in flight.
218 * (We assume that this is actually only executed once.)
219 */
220 reply_msg.acpted_rply.ar_verf = _null_auth;
221 reply_msg.acpted_rply.ar_results.where = resultsp;
222 reply_msg.acpted_rply.ar_results.proc = xresults;
223 mask = 1 << cu->cu_sock;
224rcv_again:
225 readfds = mask;
226 switch (select(32, &readfds, (int *)NULL, (int *)NULL,
227 &(cu->cu_wait))) {
228
229 case 0:
230 time_waited.tv_sec += cu->cu_wait.tv_sec;
231 time_waited.tv_usec += cu->cu_wait.tv_usec;
232 while (time_waited.tv_usec >= 1000000) {
233 time_waited.tv_sec++;
234 time_waited.tv_usec -= 1000000;
235 }
236 if ((time_waited.tv_sec < timeout.tv_sec) ||
237 ((time_waited.tv_sec == timeout.tv_sec) &&
238 (time_waited.tv_usec < timeout.tv_usec)))
239 continue;
240 return (cu->cu_error.re_status = RPC_TIMEDOUT);
241
242 case -1:
243 if (errno == EINTR)
244 goto rcv_again;
245 cu->cu_error.re_errno = errno;
246 return (cu->cu_error.re_status = RPC_CANTRECV);
247 }
248 if ((readfds & mask) == 0)
249 goto rcv_again;
250tryagain:
251 fromlen = sizeof(struct sockaddr);
252 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, UDPMSGSIZE, 0,
253 (struct sockaddr *)&from, &fromlen);
254 if (inlen < 0) {
255 if (errno == EINTR)
256 goto tryagain;
257 cu->cu_error.re_errno = errno;
258 return (cu->cu_error.re_status = RPC_CANTRECV);
259 }
260 if (inlen < sizeof(u_long))
261 goto rcv_again;
262 /* see if reply transaction id matches sent id */
263 if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf)))
264 goto rcv_again;
265 /* we now assume we have the proper reply */
266 break;
267 }
268
269 /*
270 * now decode and validate the response
271 */
272 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
273 ok = xdr_replymsg(&reply_xdrs, &reply_msg);
274 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
275 if (ok) {
276 _seterr_reply(&reply_msg, &(cu->cu_error));
277 if (cu->cu_error.re_status == RPC_SUCCESS) {
278 if (! AUTH_VALIDATE(cl->cl_auth,
279 &reply_msg.acpted_rply.ar_verf)) {
280 cu->cu_error.re_status = RPC_AUTHERROR;
281 cu->cu_error.re_why = AUTH_INVALIDRESP;
282 }
283 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
284 xdrs->x_op = XDR_FREE;
285 (void)xdr_opaque_auth(xdrs,
286 &(reply_msg.acpted_rply.ar_verf));
287 }
288 } /* end successful completion */
289 else {
290 /* maybe our credentials need to be refreshed ... */
291 if (AUTH_REFRESH(cl->cl_auth))
292 goto call_again;
293 } /* end of unsuccessful completion */
294 } /* end of valid reply message */
295 else {
296 cu->cu_error.re_status = RPC_CANTDECODERES;
297 }
298 return (cu->cu_error.re_status);
299}
300
301static void
302clntudp_geterr(cl, errp)
303 CLIENT *cl;
304 struct rpc_err *errp;
305{
306 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
307
308 *errp = cu->cu_error;
309}
310
311
312static bool_t
313clntudp_freeres(cl, xdr_res, res_ptr)
314 CLIENT *cl;
315 xdrproc_t xdr_res;
316 caddr_t res_ptr;
317{
318 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
319 register XDR *xdrs = &(cu->cu_outxdrs);
320
321 xdrs->x_op = XDR_FREE;
322 return ((*xdr_res)(xdrs, res_ptr));
323}
324
325static void
326clntudp_abort(/*h*/)
327 /*CLIENT *h;*/
328{
329}
330
331static void
332clntudp_destroy(cl)
333 CLIENT *cl;
334{
335 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
336
337 XDR_DESTROY(&(cu->cu_outxdrs));
338 mem_free((caddr_t)cu, sizeof(*cu));
339 mem_free((caddr_t)cl, sizeof(CLIENT));
340}