Add diclaimer of copyright to _osname() manual page.
[unix-history] / lib / librpc / rpc / clnt_udp.c
CommitLineData
15637ed4
RG
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 */
d2bc008e
C
29
30#if defined(LIBC_SCCS) && !defined(lint)
31/*static char *sccsid = "from: @(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";*/
32/*static char *sccsid = "from: @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC";*/
33static char *rcsid = "$Id: clnt_udp.c,v 1.3 1993/08/26 00:53:21 jtc Exp $";
15637ed4
RG
34#endif
35
36/*
37 * clnt_udp.c, Implements a UDP/IP based, client side RPC.
38 *
39 * Copyright (C) 1984, Sun Microsystems, Inc.
40 */
41
42#include <stdio.h>
43#include <rpc/rpc.h>
44#include <sys/socket.h>
45#include <sys/ioctl.h>
46#include <netdb.h>
47#include <errno.h>
48#include <rpc/pmap_clnt.h>
49
50extern int errno;
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
71/*
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
100 * no response has been heard; retransmition occurs until the actual
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
209static enum clnt_stat
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 (;;) {
290 readfds = mask;
291 switch (select(_rpc_dtablesize(), &readfds, (int *)NULL,
292 (int *)NULL, &(cu->cu_wait))) {
293
294 case 0:
295 time_waited.tv_sec += cu->cu_wait.tv_sec;
296 time_waited.tv_usec += cu->cu_wait.tv_usec;
297 while (time_waited.tv_usec >= 1000000) {
298 time_waited.tv_sec++;
299 time_waited.tv_usec -= 1000000;
300 }
301 if ((time_waited.tv_sec < timeout.tv_sec) ||
302 ((time_waited.tv_sec == timeout.tv_sec) &&
303 (time_waited.tv_usec < timeout.tv_usec)))
304 goto send_again;
305 return (cu->cu_error.re_status = RPC_TIMEDOUT);
306
307 /*
308 * buggy in other cases because time_waited is not being
309 * updated.
310 */
311 case -1:
312 if (errno == EINTR)
313 continue;
314 cu->cu_error.re_errno = errno;
315 return (cu->cu_error.re_status = RPC_CANTRECV);
316 }
317 do {
318 fromlen = sizeof(struct sockaddr);
319 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
320 (int) cu->cu_recvsz, 0,
321 (struct sockaddr *)&from, &fromlen);
322 } while (inlen < 0 && errno == EINTR);
323 if (inlen < 0) {
324 if (errno == EWOULDBLOCK)
325 continue;
326 cu->cu_error.re_errno = errno;
327 return (cu->cu_error.re_status = RPC_CANTRECV);
328 }
329 if (inlen < sizeof(u_long))
330 continue;
331 /* see if reply transaction id matches sent id */
332 if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf)))
333 continue;
334 /* we now assume we have the proper reply */
335 break;
336 }
337
338 /*
339 * now decode and validate the response
340 */
341 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
342 ok = xdr_replymsg(&reply_xdrs, &reply_msg);
343 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
344 if (ok) {
345 _seterr_reply(&reply_msg, &(cu->cu_error));
346 if (cu->cu_error.re_status == RPC_SUCCESS) {
347 if (! AUTH_VALIDATE(cl->cl_auth,
348 &reply_msg.acpted_rply.ar_verf)) {
349 cu->cu_error.re_status = RPC_AUTHERROR;
350 cu->cu_error.re_why = AUTH_INVALIDRESP;
351 }
352 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
353 xdrs->x_op = XDR_FREE;
354 (void)xdr_opaque_auth(xdrs,
355 &(reply_msg.acpted_rply.ar_verf));
356 }
357 } /* end successful completion */
358 else {
359 /* maybe our credentials need to be refreshed ... */
360 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
361 nrefreshes--;
362 goto call_again;
363 }
364 } /* end of unsuccessful completion */
365 } /* end of valid reply message */
366 else {
367 cu->cu_error.re_status = RPC_CANTDECODERES;
368 }
369 return (cu->cu_error.re_status);
370}
371
372static void
373clntudp_geterr(cl, errp)
374 CLIENT *cl;
375 struct rpc_err *errp;
376{
377 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
378
379 *errp = cu->cu_error;
380}
381
382
383static bool_t
384clntudp_freeres(cl, xdr_res, res_ptr)
385 CLIENT *cl;
386 xdrproc_t xdr_res;
387 caddr_t res_ptr;
388{
389 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
390 register XDR *xdrs = &(cu->cu_outxdrs);
391
392 xdrs->x_op = XDR_FREE;
393 return ((*xdr_res)(xdrs, res_ptr));
394}
395
396static void
397clntudp_abort(/*h*/)
398 /*CLIENT *h;*/
399{
400}
401
402static bool_t
403clntudp_control(cl, request, info)
404 CLIENT *cl;
405 int request;
406 char *info;
407{
408 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
409
410 switch (request) {
411 case CLSET_TIMEOUT:
412 cu->cu_total = *(struct timeval *)info;
413 break;
414 case CLGET_TIMEOUT:
415 *(struct timeval *)info = cu->cu_total;
416 break;
417 case CLSET_RETRY_TIMEOUT:
418 cu->cu_wait = *(struct timeval *)info;
419 break;
420 case CLGET_RETRY_TIMEOUT:
421 *(struct timeval *)info = cu->cu_wait;
422 break;
423 case CLGET_SERVER_ADDR:
424 *(struct sockaddr_in *)info = cu->cu_raddr;
425 break;
426 default:
427 return (FALSE);
428 }
429 return (TRUE);
430}
431
432static void
433clntudp_destroy(cl)
434 CLIENT *cl;
435{
436 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
437
438 if (cu->cu_closeit) {
439 (void)close(cu->cu_sock);
440 }
441 XDR_DESTROY(&(cu->cu_outxdrs));
442 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
443 mem_free((caddr_t)cl, sizeof(CLIENT));
444}