GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/rpc/clnt_udp.c Lines: 0 166 0.0 %
Date: 2017-11-07 Branches: 0 101 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: clnt_udp.c,v 1.32 2015/11/01 03:45:29 guenther Exp $ */
2
3
/*
4
 * Copyright (c) 2010, Oracle America, Inc.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are
8
 * met:
9
 *
10
 *     * Redistributions of source code must retain the above copyright
11
 *       notice, this list of conditions and the following disclaimer.
12
 *     * Redistributions in binary form must reproduce the above
13
 *       copyright notice, this list of conditions and the following
14
 *       disclaimer in the documentation and/or other materials
15
 *       provided with the distribution.
16
 *     * Neither the name of the "Oracle America, Inc." nor the names of its
17
 *       contributors may be used to endorse or promote products derived
18
 *       from this software without specific prior written permission.
19
 *
20
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23
 *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24
 *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25
 *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
 *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27
 *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
 *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29
 *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
 *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
 */
33
34
/*
35
 * clnt_udp.c, Implements a UDP/IP based, client side RPC.
36
 */
37
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <unistd.h>
42
#include <fcntl.h>
43
#include <rpc/rpc.h>
44
#include <sys/socket.h>
45
#include <netdb.h>
46
#include <errno.h>
47
#include <rpc/pmap_clnt.h>
48
49
/*
50
 * UDP bases client side rpc operations
51
 */
52
static enum clnt_stat	clntudp_call(CLIENT *, u_long, xdrproc_t, caddr_t,
53
			    xdrproc_t, caddr_t, struct timeval);
54
static void		clntudp_abort(CLIENT *);
55
static void		clntudp_geterr(CLIENT *, struct rpc_err *);
56
static bool_t		clntudp_freeres(CLIENT *, xdrproc_t, caddr_t);
57
static bool_t           clntudp_control(CLIENT *, u_int, void *);
58
static void		clntudp_destroy(CLIENT *);
59
60
static struct clnt_ops udp_ops = {
61
	clntudp_call,
62
	clntudp_abort,
63
	clntudp_geterr,
64
	clntudp_freeres,
65
	clntudp_destroy,
66
	clntudp_control
67
};
68
69
/*
70
 * Private data kept per client handle
71
 */
72
struct cu_data {
73
	int		   cu_sock;
74
	bool_t		   cu_closeit;
75
	struct sockaddr_in cu_raddr;
76
	int		   cu_rlen;
77
	struct timeval	   cu_wait;
78
	struct timeval     cu_total;
79
	struct rpc_err	   cu_error;
80
	XDR		   cu_outxdrs;
81
	u_int		   cu_xdrpos;
82
	u_int		   cu_sendsz;
83
	char		   *cu_outbuf;
84
	u_int		   cu_recvsz;
85
	char		   cu_inbuf[1];
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 client's responsibility to close *sockp, unless
94
 *	clntudp_bufcreate() was called with *sockp = -1 (so it created
95
 *	the socket), and CLNT_DESTROY() is used.
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;  retransmission 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
 */
106
CLIENT *
107
clntudp_bufcreate(struct sockaddr_in *raddr, u_long program, u_long version,
108
    struct timeval wait, int *sockp, u_int sendsz, u_int recvsz)
109
{
110
	CLIENT *cl;
111
	struct cu_data *cu = NULL;
112
	struct timeval now;
113
	struct rpc_msg call_msg;
114
115
	cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
116
	if (cl == NULL) {
117
		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
118
		rpc_createerr.cf_error.re_errno = errno;
119
		goto fooy;
120
	}
121
	sendsz = ((sendsz + 3) / 4) * 4;
122
	recvsz = ((recvsz + 3) / 4) * 4;
123
	cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
124
	if (cu == NULL) {
125
		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
126
		rpc_createerr.cf_error.re_errno = errno;
127
		goto fooy;
128
	}
129
	cu->cu_outbuf = &cu->cu_inbuf[recvsz];
130
131
	(void)gettimeofday(&now, NULL);
132
	if (raddr->sin_port == 0) {
133
		u_short port;
134
		if ((port =
135
		    pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
136
			goto fooy;
137
		}
138
		raddr->sin_port = htons(port);
139
	}
140
	cl->cl_ops = &udp_ops;
141
	cl->cl_private = (caddr_t)cu;
142
	cu->cu_raddr = *raddr;
143
	cu->cu_rlen = sizeof (cu->cu_raddr);
144
	cu->cu_wait = wait;
145
	cu->cu_total.tv_sec = -1;
146
	cu->cu_total.tv_usec = -1;
147
	cu->cu_sendsz = sendsz;
148
	cu->cu_recvsz = recvsz;
149
	call_msg.rm_xid = arc4random();
150
	call_msg.rm_direction = CALL;
151
	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
152
	call_msg.rm_call.cb_prog = program;
153
	call_msg.rm_call.cb_vers = version;
154
	xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
155
	    sendsz, XDR_ENCODE);
156
	if (!xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
157
		goto fooy;
158
	}
159
	cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
160
	if (*sockp < 0) {
161
		*sockp = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK,
162
		    IPPROTO_UDP);
163
		if (*sockp < 0) {
164
			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
165
			rpc_createerr.cf_error.re_errno = errno;
166
			goto fooy;
167
		}
168
		/* attempt to bind to priv port */
169
		(void)bindresvport(*sockp, NULL);
170
		cu->cu_closeit = TRUE;
171
	} else {
172
		cu->cu_closeit = FALSE;
173
	}
174
	cu->cu_sock = *sockp;
175
	cl->cl_auth = authnone_create();
176
	if (cl->cl_auth == NULL) {
177
		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
178
		rpc_createerr.cf_error.re_errno = errno;
179
		goto fooy;
180
	}
181
	return (cl);
182
fooy:
183
	if (cu)
184
		mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);
185
	if (cl)
186
		mem_free((caddr_t)cl, sizeof(CLIENT));
187
	return (NULL);
188
}
189
DEF_WEAK(clntudp_bufcreate);
190
191
CLIENT *
192
clntudp_create(struct sockaddr_in *raddr, u_long program, u_long version,
193
    struct timeval wait, int *sockp)
194
{
195
196
	return(clntudp_bufcreate(raddr, program, version, wait, sockp,
197
	    UDPMSGSIZE, UDPMSGSIZE));
198
}
199
DEF_WEAK(clntudp_create);
200
201
static enum clnt_stat
202
clntudp_call(CLIENT *cl,	/* client handle */
203
    u_long proc,		/* procedure number */
204
    xdrproc_t xargs,		/* xdr routine for args */
205
    caddr_t argsp,		/* pointer to args */
206
    xdrproc_t xresults,		/* xdr routine for results */
207
    caddr_t resultsp,		/* pointer to results */
208
    struct timeval utimeout)	/* seconds to wait before giving up */
209
{
210
	struct cu_data *cu = (struct cu_data *)cl->cl_private;
211
	XDR *xdrs;
212
	int outlen;
213
	int inlen;
214
	socklen_t fromlen;
215
	struct pollfd pfd[1];
216
	struct sockaddr_in from;
217
	struct rpc_msg reply_msg;
218
	XDR reply_xdrs;
219
	struct timeval time_waited, start, after, tmp1, tmp2;
220
	bool_t ok;
221
	int nrefreshes = 2;	/* number of times to refresh cred */
222
	struct timeval timeout;
223
224
	if (cu->cu_total.tv_usec == -1)
225
		timeout = utimeout;     /* use supplied timeout */
226
	else
227
		timeout = cu->cu_total; /* use default timeout */
228
229
	pfd[0].fd = cu->cu_sock;
230
	pfd[0].events = POLLIN;
231
	timerclear(&time_waited);
232
call_again:
233
	xdrs = &(cu->cu_outxdrs);
234
	xdrs->x_op = XDR_ENCODE;
235
	XDR_SETPOS(xdrs, cu->cu_xdrpos);
236
	/*
237
	 * the transaction is the first thing in the out buffer
238
	 */
239
	(*(u_short *)(cu->cu_outbuf))++;
240
	if (!XDR_PUTLONG(xdrs, (long *)&proc) ||
241
	    !AUTH_MARSHALL(cl->cl_auth, xdrs) ||
242
	    !(*xargs)(xdrs, argsp)) {
243
		return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
244
	}
245
	outlen = (int)XDR_GETPOS(xdrs);
246
247
send_again:
248
	if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
249
	    (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen) {
250
		cu->cu_error.re_errno = errno;
251
		return (cu->cu_error.re_status = RPC_CANTSEND);
252
	}
253
254
	/*
255
	 * Hack to provide rpc-based message passing
256
	 */
257
	if (!timerisset(&timeout))
258
		return (cu->cu_error.re_status = RPC_TIMEDOUT);
259
260
	/*
261
	 * sub-optimal code appears here because we have
262
	 * some clock time to spare while the packets are in flight.
263
	 * (We assume that this is actually only executed once.)
264
	 */
265
	reply_msg.acpted_rply.ar_verf = _null_auth;
266
	reply_msg.acpted_rply.ar_results.where = resultsp;
267
	reply_msg.acpted_rply.ar_results.proc = xresults;
268
269
	gettimeofday(&start, NULL);
270
	for (;;) {
271
		switch (poll(pfd, 1,
272
		    cu->cu_wait.tv_sec * 1000 + cu->cu_wait.tv_usec / 1000)) {
273
		case 0:
274
			timeradd(&time_waited, &cu->cu_wait, &tmp1);
275
			time_waited = tmp1;
276
			if (timercmp(&time_waited, &timeout, <))
277
				goto send_again;
278
			return (cu->cu_error.re_status = RPC_TIMEDOUT);
279
		case 1:
280
			if (pfd[0].revents & POLLNVAL)
281
				errno = EBADF;
282
			else if (pfd[0].revents & POLLERR)
283
				errno = EIO;
284
			else
285
				break;
286
			/* FALLTHROUGH */
287
		case -1:
288
			if (errno == EINTR) {
289
				gettimeofday(&after, NULL);
290
				timersub(&after, &start, &tmp1);
291
				timeradd(&time_waited, &tmp1, &tmp2);
292
				time_waited = tmp2;
293
				if (timercmp(&time_waited, &timeout, <))
294
					continue;
295
				return (cu->cu_error.re_status = RPC_TIMEDOUT);
296
			}
297
			cu->cu_error.re_errno = errno;
298
			return (cu->cu_error.re_status = RPC_CANTRECV);
299
		}
300
301
		do {
302
			fromlen = sizeof(struct sockaddr);
303
			inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
304
			    (int) cu->cu_recvsz, 0,
305
			    (struct sockaddr *)&from, &fromlen);
306
		} while (inlen < 0 && errno == EINTR);
307
		if (inlen < 0) {
308
			if (errno == EWOULDBLOCK)
309
				continue;
310
			cu->cu_error.re_errno = errno;
311
			return (cu->cu_error.re_status = RPC_CANTRECV);
312
		}
313
		if (inlen < sizeof(u_int32_t))
314
			continue;
315
		/* see if reply transaction id matches sent id */
316
		if (((struct rpc_msg *)(cu->cu_inbuf))->rm_xid !=
317
		    ((struct rpc_msg *)(cu->cu_outbuf))->rm_xid)
318
			continue;
319
		/* we now assume we have the proper reply */
320
		break;
321
	}
322
323
	/*
324
	 * now decode and validate the response
325
	 */
326
	xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
327
	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
328
	/* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
329
	if (ok) {
330
#if 0
331
		/*
332
		 * XXX Would like to check these, but call_msg is not
333
		 * around.
334
		 */
335
		if (reply_msg.rm_call.cb_prog != call_msg.rm_call.cb_prog ||
336
		    reply_msg.rm_call.cb_vers != call_msg.rm_call.cb_vers ||
337
		    reply_msg.rm_call.cb_proc != call_msg.rm_call.cb_proc) {
338
			goto call_again;	/* XXX spin? */
339
		}
340
#endif
341
342
		_seterr_reply(&reply_msg, &(cu->cu_error));
343
		if (cu->cu_error.re_status == RPC_SUCCESS) {
344
			if (!AUTH_VALIDATE(cl->cl_auth,
345
			    &reply_msg.acpted_rply.ar_verf)) {
346
				cu->cu_error.re_status = RPC_AUTHERROR;
347
				cu->cu_error.re_why = AUTH_INVALIDRESP;
348
			}
349
			if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
350
				xdrs->x_op = XDR_FREE;
351
				(void)xdr_opaque_auth(xdrs,
352
				    &(reply_msg.acpted_rply.ar_verf));
353
			}
354
		} else {
355
			/* maybe our credentials need to be refreshed ... */
356
			if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
357
				nrefreshes--;
358
				goto call_again;
359
			}
360
		}
361
	} else {
362
		/* xdr_replymsg() may have left some things allocated */
363
		int op = reply_xdrs.x_op;
364
		reply_xdrs.x_op = XDR_FREE;
365
		xdr_replymsg(&reply_xdrs, &reply_msg);
366
		reply_xdrs.x_op = op;
367
		cu->cu_error.re_status = RPC_CANTDECODERES;
368
	}
369
370
	return (cu->cu_error.re_status);
371
}
372
373
static void
374
clntudp_geterr(CLIENT *cl, struct rpc_err *errp)
375
{
376
	struct cu_data *cu = (struct cu_data *)cl->cl_private;
377
378
	*errp = cu->cu_error;
379
}
380
381
382
static bool_t
383
clntudp_freeres(CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr)
384
{
385
	struct cu_data *cu = (struct cu_data *)cl->cl_private;
386
	XDR *xdrs = &(cu->cu_outxdrs);
387
388
	xdrs->x_op = XDR_FREE;
389
	return ((*xdr_res)(xdrs, res_ptr));
390
}
391
392
static void
393
clntudp_abort(CLIENT *clnt)
394
{
395
}
396
397
static bool_t
398
clntudp_control(CLIENT *cl, u_int request, void *info)
399
{
400
	struct cu_data *cu = (struct cu_data *)cl->cl_private;
401
402
	switch (request) {
403
	case CLSET_TIMEOUT:
404
		cu->cu_total = *(struct timeval *)info;
405
		break;
406
	case CLGET_TIMEOUT:
407
		*(struct timeval *)info = cu->cu_total;
408
		break;
409
	case CLSET_RETRY_TIMEOUT:
410
		cu->cu_wait = *(struct timeval *)info;
411
		break;
412
	case CLGET_RETRY_TIMEOUT:
413
		*(struct timeval *)info = cu->cu_wait;
414
		break;
415
	case CLGET_SERVER_ADDR:
416
		*(struct sockaddr_in *)info = cu->cu_raddr;
417
		break;
418
	default:
419
		return (FALSE);
420
	}
421
	return (TRUE);
422
}
423
424
static void
425
clntudp_destroy(CLIENT *cl)
426
{
427
	struct cu_data *cu = (struct cu_data *)cl->cl_private;
428
429
	if (cu->cu_closeit && cu->cu_sock != -1) {
430
		(void)close(cu->cu_sock);
431
	}
432
	XDR_DESTROY(&(cu->cu_outxdrs));
433
	mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
434
	mem_free((caddr_t)cl, sizeof(CLIENT));
435
}