]> git.ipfire.org Git - thirdparty/glibc.git/blame - sunrpc/clnt_udp.c
update from main archive 970209
[thirdparty/glibc.git] / sunrpc / clnt_udp.c
CommitLineData
28f540f4
RM
1/* @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC */
2/*
3 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
4 * unrestricted use provided that this legend is included on all tape
5 * media and as a part of the software program in whole or part. Users
6 * may copy or modify Sun RPC without charge, but are not authorized
7 * to license or distribute it to anyone else except as part of a product or
8 * program developed by the user.
c4029823 9 *
28f540f4
RM
10 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
11 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
12 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
c4029823 13 *
28f540f4
RM
14 * Sun RPC is provided with no support and without any obligation on the
15 * part of Sun Microsystems, Inc. to assist in its use, correction,
16 * modification or enhancement.
c4029823 17 *
28f540f4
RM
18 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
19 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
20 * OR ANY PART THEREOF.
c4029823 21 *
28f540f4
RM
22 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
23 * or profits or other special, indirect and consequential damages, even if
24 * Sun has been advised of the possibility of such damages.
c4029823 25 *
28f540f4
RM
26 * Sun Microsystems, Inc.
27 * 2550 Garcia Avenue
28 * Mountain View, California 94043
29 */
30#if !defined(lint) && defined(SCCSIDS)
31static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";
32#endif
33
34/*
35 * clnt_udp.c, Implements a UDP/IP based, client side RPC.
36 *
37 * Copyright (C) 1984, Sun Microsystems, Inc.
38 */
39
40#include <stdio.h>
41#include <rpc/rpc.h>
42#include <sys/socket.h>
43#include <sys/ioctl.h>
44#include <netdb.h>
45#include <errno.h>
46#include <rpc/pmap_clnt.h>
47
c4029823 48#ifndef errno
28f540f4 49extern int errno;
c4029823 50#endif
28f540f4
RM
51
52/*
53 * UDP bases client side rpc operations
54 */
55static enum clnt_stat clntudp_call();
56static void clntudp_abort();
57static void clntudp_geterr();
58static bool_t clntudp_freeres();
59static bool_t clntudp_control();
60static void clntudp_destroy();
61
62static struct clnt_ops udp_ops = {
63 clntudp_call,
64 clntudp_abort,
65 clntudp_geterr,
66 clntudp_freeres,
67 clntudp_destroy,
68 clntudp_control
69};
70
c4029823 71/*
28f540f4
RM
72 * Private data kept per client handle
73 */
74struct cu_data {
75 int cu_sock;
76 bool_t cu_closeit;
77 struct sockaddr_in cu_raddr;
78 int cu_rlen;
79 struct timeval cu_wait;
80 struct timeval cu_total;
81 struct rpc_err cu_error;
82 XDR cu_outxdrs;
83 u_int cu_xdrpos;
84 u_int cu_sendsz;
85 char *cu_outbuf;
86 u_int cu_recvsz;
87 char cu_inbuf[1];
88};
89
90/*
91 * Create a UDP based client handle.
92 * If *sockp<0, *sockp is set to a newly created UPD socket.
93 * If raddr->sin_port is 0 a binder on the remote machine
94 * is consulted for the correct port number.
95 * NB: It is the clients responsibility to close *sockp.
96 * NB: The rpch->cl_auth is initialized to null authentication.
97 * Caller may wish to set this something more useful.
98 *
99 * wait is the amount of time used between retransmitting a call if
6d52618b 100 * no response has been heard; retransmission occurs until the actual
28f540f4
RM
101 * rpc call times out.
102 *
103 * sendsz and recvsz are the maximum allowable packet sizes that can be
104 * sent and received.
105 */
106CLIENT *
107clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
108 struct sockaddr_in *raddr;
109 u_long program;
110 u_long version;
111 struct timeval wait;
112 register int *sockp;
113 u_int sendsz;
114 u_int recvsz;
115{
116 CLIENT *cl;
117 register struct cu_data *cu;
118 struct timeval now;
119 struct rpc_msg call_msg;
120
121 cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
122 if (cl == NULL) {
123 (void) 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 sendsz = ((sendsz + 3) / 4) * 4;
129 recvsz = ((recvsz + 3) / 4) * 4;
130 cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
131 if (cu == NULL) {
132 (void) fprintf(stderr, "clntudp_create: out of memory\n");
133 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
134 rpc_createerr.cf_error.re_errno = errno;
135 goto fooy;
136 }
137 cu->cu_outbuf = &cu->cu_inbuf[recvsz];
138
139 (void)gettimeofday(&now, (struct timezone *)0);
140 if (raddr->sin_port == 0) {
141 u_short port;
142 if ((port =
143 pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
144 goto fooy;
145 }
146 raddr->sin_port = htons(port);
147 }
148 cl->cl_ops = &udp_ops;
149 cl->cl_private = (caddr_t)cu;
150 cu->cu_raddr = *raddr;
151 cu->cu_rlen = sizeof (cu->cu_raddr);
152 cu->cu_wait = wait;
153 cu->cu_total.tv_sec = -1;
154 cu->cu_total.tv_usec = -1;
155 cu->cu_sendsz = sendsz;
156 cu->cu_recvsz = recvsz;
157 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
158 call_msg.rm_direction = CALL;
159 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
160 call_msg.rm_call.cb_prog = program;
161 call_msg.rm_call.cb_vers = version;
162 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
163 sendsz, XDR_ENCODE);
164 if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
165 goto fooy;
166 }
167 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
168 if (*sockp < 0) {
169 int dontblock = 1;
170
171 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
172 if (*sockp < 0) {
173 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
174 rpc_createerr.cf_error.re_errno = errno;
175 goto fooy;
176 }
177 /* attempt to bind to prov port */
178 (void)bindresvport(*sockp, (struct sockaddr_in *)0);
179 /* the sockets rpc controls are non-blocking */
180 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock);
181 cu->cu_closeit = TRUE;
182 } else {
183 cu->cu_closeit = FALSE;
184 }
185 cu->cu_sock = *sockp;
186 cl->cl_auth = authnone_create();
187 return (cl);
188fooy:
189 if (cu)
190 mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);
191 if (cl)
192 mem_free((caddr_t)cl, sizeof(CLIENT));
193 return ((CLIENT *)NULL);
194}
195
196CLIENT *
197clntudp_create(raddr, program, version, wait, sockp)
198 struct sockaddr_in *raddr;
199 u_long program;
200 u_long version;
201 struct timeval wait;
202 register int *sockp;
203{
204
205 return(clntudp_bufcreate(raddr, program, version, wait, sockp,
206 UDPMSGSIZE, UDPMSGSIZE));
207}
208
c4029823 209static enum clnt_stat
28f540f4
RM
210clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
211 register CLIENT *cl; /* client handle */
212 u_long proc; /* procedure number */
213 xdrproc_t xargs; /* xdr routine for args */
214 caddr_t argsp; /* pointer to args */
215 xdrproc_t xresults; /* xdr routine for results */
216 caddr_t resultsp; /* pointer to results */
217 struct timeval utimeout; /* seconds to wait before giving up */
218{
219 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
220 register XDR *xdrs;
221 register int outlen;
222 register int inlen;
223 int fromlen;
224#ifdef FD_SETSIZE
225 fd_set readfds;
226 fd_set mask;
227#else
228 int readfds;
229 register int mask;
230#endif /* def FD_SETSIZE */
231 struct sockaddr_in from;
232 struct rpc_msg reply_msg;
233 XDR reply_xdrs;
234 struct timeval time_waited;
235 bool_t ok;
236 int nrefreshes = 2; /* number of times to refresh cred */
237 struct timeval timeout;
238
239 if (cu->cu_total.tv_usec == -1) {
240 timeout = utimeout; /* use supplied timeout */
241 } else {
242 timeout = cu->cu_total; /* use default timeout */
243 }
244
245 time_waited.tv_sec = 0;
246 time_waited.tv_usec = 0;
247call_again:
248 xdrs = &(cu->cu_outxdrs);
249 xdrs->x_op = XDR_ENCODE;
250 XDR_SETPOS(xdrs, cu->cu_xdrpos);
251 /*
252 * the transaction is the first thing in the out buffer
253 */
254 (*(u_short *)(cu->cu_outbuf))++;
255 if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
256 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
257 (! (*xargs)(xdrs, argsp)))
258 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
259 outlen = (int)XDR_GETPOS(xdrs);
260
261send_again:
262 if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
263 (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen)
264 != outlen) {
265 cu->cu_error.re_errno = errno;
266 return (cu->cu_error.re_status = RPC_CANTSEND);
267 }
268
269 /*
270 * Hack to provide rpc-based message passing
271 */
272 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
273 return (cu->cu_error.re_status = RPC_TIMEDOUT);
274 }
275 /*
276 * sub-optimal code appears here because we have
277 * some clock time to spare while the packets are in flight.
278 * (We assume that this is actually only executed once.)
279 */
280 reply_msg.acpted_rply.ar_verf = _null_auth;
281 reply_msg.acpted_rply.ar_results.where = resultsp;
282 reply_msg.acpted_rply.ar_results.proc = xresults;
283#ifdef FD_SETSIZE
284 FD_ZERO(&mask);
285 FD_SET(cu->cu_sock, &mask);
286#else
287 mask = 1 << cu->cu_sock;
288#endif /* def FD_SETSIZE */
289 for (;;) {
7cc27f44 290 struct timeval timeout = cu->cu_wait;
28f540f4 291 readfds = mask;
c4029823 292 switch (select(_rpc_dtablesize(), &readfds, (int *)NULL,
7cc27f44 293 (int *)NULL, &timeout)) {
28f540f4
RM
294
295 case 0:
296 time_waited.tv_sec += cu->cu_wait.tv_sec;
297 time_waited.tv_usec += cu->cu_wait.tv_usec;
298 while (time_waited.tv_usec >= 1000000) {
299 time_waited.tv_sec++;
300 time_waited.tv_usec -= 1000000;
301 }
302 if ((time_waited.tv_sec < timeout.tv_sec) ||
303 ((time_waited.tv_sec == timeout.tv_sec) &&
304 (time_waited.tv_usec < timeout.tv_usec)))
c4029823 305 goto send_again;
28f540f4
RM
306 return (cu->cu_error.re_status = RPC_TIMEDOUT);
307
308 /*
309 * buggy in other cases because time_waited is not being
310 * updated.
311 */
312 case -1:
313 if (errno == EINTR)
c4029823 314 continue;
28f540f4
RM
315 cu->cu_error.re_errno = errno;
316 return (cu->cu_error.re_status = RPC_CANTRECV);
317 }
318 do {
319 fromlen = sizeof(struct sockaddr);
c4029823 320 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
28f540f4
RM
321 (int) cu->cu_recvsz, 0,
322 (struct sockaddr *)&from, &fromlen);
323 } while (inlen < 0 && errno == EINTR);
324 if (inlen < 0) {
325 if (errno == EWOULDBLOCK)
c4029823 326 continue;
28f540f4
RM
327 cu->cu_error.re_errno = errno;
328 return (cu->cu_error.re_status = RPC_CANTRECV);
329 }
b20e47cb 330 if (inlen < 4)
c4029823 331 continue;
28f540f4 332 /* see if reply transaction id matches sent id */
b20e47cb 333 if (*((u_int32_t *)(cu->cu_inbuf)) != *((u_int32_t *)(cu->cu_outbuf)))
c4029823 334 continue;
28f540f4
RM
335 /* we now assume we have the proper reply */
336 break;
337 }
338
339 /*
340 * now decode and validate the response
341 */
342 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
343 ok = xdr_replymsg(&reply_xdrs, &reply_msg);
344 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
345 if (ok) {
346 _seterr_reply(&reply_msg, &(cu->cu_error));
347 if (cu->cu_error.re_status == RPC_SUCCESS) {
348 if (! AUTH_VALIDATE(cl->cl_auth,
349 &reply_msg.acpted_rply.ar_verf)) {
350 cu->cu_error.re_status = RPC_AUTHERROR;
351 cu->cu_error.re_why = AUTH_INVALIDRESP;
352 }
353 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
354 xdrs->x_op = XDR_FREE;
355 (void)xdr_opaque_auth(xdrs,
356 &(reply_msg.acpted_rply.ar_verf));
c4029823 357 }
28f540f4
RM
358 } /* end successful completion */
359 else {
360 /* maybe our credentials need to be refreshed ... */
361 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
362 nrefreshes--;
363 goto call_again;
364 }
365 } /* end of unsuccessful completion */
366 } /* end of valid reply message */
367 else {
368 cu->cu_error.re_status = RPC_CANTDECODERES;
369 }
370 return (cu->cu_error.re_status);
371}
372
373static void
374clntudp_geterr(cl, errp)
375 CLIENT *cl;
376 struct rpc_err *errp;
377{
378 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
379
380 *errp = cu->cu_error;
381}
382
383
384static bool_t
385clntudp_freeres(cl, xdr_res, res_ptr)
386 CLIENT *cl;
387 xdrproc_t xdr_res;
388 caddr_t res_ptr;
389{
390 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
391 register XDR *xdrs = &(cu->cu_outxdrs);
392
393 xdrs->x_op = XDR_FREE;
394 return ((*xdr_res)(xdrs, res_ptr));
395}
396
c4029823 397static void
28f540f4
RM
398clntudp_abort(/*h*/)
399 /*CLIENT *h;*/
400{
401}
402
403static bool_t
404clntudp_control(cl, request, info)
405 CLIENT *cl;
406 int request;
407 char *info;
408{
409 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
410
411 switch (request) {
412 case CLSET_TIMEOUT:
413 cu->cu_total = *(struct timeval *)info;
414 break;
415 case CLGET_TIMEOUT:
416 *(struct timeval *)info = cu->cu_total;
417 break;
418 case CLSET_RETRY_TIMEOUT:
419 cu->cu_wait = *(struct timeval *)info;
420 break;
421 case CLGET_RETRY_TIMEOUT:
422 *(struct timeval *)info = cu->cu_wait;
423 break;
424 case CLGET_SERVER_ADDR:
425 *(struct sockaddr_in *)info = cu->cu_raddr;
426 break;
427 default:
428 return (FALSE);
429 }
430 return (TRUE);
431}
c4029823 432
28f540f4
RM
433static void
434clntudp_destroy(cl)
435 CLIENT *cl;
436{
437 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
438
439 if (cu->cu_closeit) {
440 (void)close(cu->cu_sock);
441 }
442 XDR_DESTROY(&(cu->cu_outxdrs));
443 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
444 mem_free((caddr_t)cl, sizeof(CLIENT));
445}