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

Line Branch Exec Source
1
/*	$OpenBSD: svc_udp.c,v 1.25 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
 * svc_udp.c,
36
 * Server side for UDP/IP based RPC.  (Does some caching in the hopes of
37
 * achieving execute-at-most-once semantics.)
38
 */
39
40
#include <stdio.h>
41
#include <stdlib.h>
42
#include <string.h>
43
#include <rpc/rpc.h>
44
#include <sys/socket.h>
45
#include <stdint.h>
46
#include <errno.h>
47
#include <unistd.h>
48
49
50
#define rpc_buffer(xprt) ((xprt)->xp_p1)
51
#define MAX(a, b)     ((a > b) ? a : b)
52
53
static bool_t		svcudp_recv(SVCXPRT *, struct rpc_msg *);
54
static enum xprt_stat	svcudp_stat(SVCXPRT *);
55
static bool_t		svcudp_getargs(SVCXPRT *, xdrproc_t, caddr_t);
56
static bool_t		svcudp_reply(SVCXPRT *, struct rpc_msg *);
57
static bool_t		svcudp_freeargs(SVCXPRT *, xdrproc_t, caddr_t);
58
static void		svcudp_destroy(SVCXPRT *);
59
static void		cache_set(SVCXPRT *, u_long);
60
static int		cache_get(SVCXPRT *, struct rpc_msg *, char **,
61
			    u_long *);
62
63
static struct xp_ops svcudp_op = {
64
	svcudp_recv,
65
	svcudp_stat,
66
	svcudp_getargs,
67
	svcudp_reply,
68
	svcudp_freeargs,
69
	svcudp_destroy
70
};
71
72
/*
73
 * kept in xprt->xp_p2
74
 */
75
struct svcudp_data {
76
	u_int   su_iosz;	/* byte size of send.recv buffer */
77
	u_long	su_xid;		/* transaction id */
78
	XDR	su_xdrs;	/* XDR handle */
79
	char	su_verfbody[MAX_AUTH_BYTES];	/* verifier body */
80
	char * 	su_cache;	/* cached data, NULL if no cache */
81
};
82
#define	su_data(xprt)	((struct svcudp_data *)(xprt->xp_p2))
83
84
/*
85
 * Usage:
86
 *	xprt = svcudp_create(sock);
87
 *
88
 * If sock<0 then a socket is created, else sock is used.
89
 * If the socket, sock is not bound to a port then svcudp_create
90
 * binds it to an arbitrary port.  In any (successful) case,
91
 * xprt->xp_sock is the registered socket number and xprt->xp_port is the
92
 * associated port number.
93
 * Once *xprt is initialized, it is registered as a transporter;
94
 * see (svc.h, xprt_register).
95
 * The routines returns NULL if a problem occurred.
96
 */
97
SVCXPRT *
98
svcudp_bufcreate(int sock, u_int sendsz, u_int recvsz)
99
{
100
	bool_t madesock = FALSE;
101
	SVCXPRT *xprt;
102
	struct svcudp_data *su;
103
	struct sockaddr_in addr;
104
	socklen_t len = sizeof(struct sockaddr_in);
105
106
	if (sock == RPC_ANYSOCK) {
107
		if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
108
			return (NULL);
109
		madesock = TRUE;
110
	}
111
	memset(&addr, 0, sizeof (addr));
112
	addr.sin_len = sizeof(struct sockaddr_in);
113
	addr.sin_family = AF_INET;
114
	if (bindresvport(sock, &addr)) {
115
		addr.sin_port = 0;
116
		(void)bind(sock, (struct sockaddr *)&addr, len);
117
	}
118
	if (getsockname(sock, (struct sockaddr *)&addr, &len) != 0) {
119
		if (madesock)
120
			(void)close(sock);
121
		return (NULL);
122
	}
123
	xprt = malloc(sizeof(SVCXPRT));
124
	if (xprt == NULL) {
125
		if (madesock)
126
			(void)close(sock);
127
		return (NULL);
128
	}
129
	su = malloc(sizeof(*su));
130
	if (su == NULL) {
131
		if (madesock)
132
			(void)close(sock);
133
		free(xprt);
134
		return (NULL);
135
	}
136
	su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4;
137
	if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL) {
138
		if (madesock)
139
			(void)close(sock);
140
		free(xprt);
141
		free(su);
142
		return (NULL);
143
	}
144
	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz,
145
	    XDR_DECODE);
146
	su->su_cache = NULL;
147
	xprt->xp_p2 = (caddr_t)su;
148
	xprt->xp_verf.oa_base = su->su_verfbody;
149
	xprt->xp_ops = &svcudp_op;
150
	xprt->xp_port = ntohs(addr.sin_port);
151
	xprt->xp_sock = sock;
152
	if (__xprt_register(xprt) == 0) {
153
		if (madesock)
154
			(void)close(sock);
155
		free(rpc_buffer(xprt));
156
		free(xprt);
157
		free(su);
158
		return (NULL);
159
	}
160
	return (xprt);
161
}
162
DEF_WEAK(svcudp_bufcreate);
163
164
SVCXPRT *
165
svcudp_create(int sock)
166
{
167
168
	return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE));
169
}
170
DEF_WEAK(svcudp_create);
171
172
static enum xprt_stat
173
svcudp_stat(SVCXPRT *xprt)
174
{
175
176
	return (XPRT_IDLE);
177
}
178
179
static bool_t
180
svcudp_recv(SVCXPRT *xprt, struct rpc_msg *msg)
181
{
182
	struct svcudp_data *su = su_data(xprt);
183
	XDR *xdrs = &(su->su_xdrs);
184
	int rlen;
185
	char *reply;
186
	u_long replylen;
187
188
    again:
189
	xprt->xp_addrlen = sizeof(struct sockaddr_in);
190
	rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz,
191
	    0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen));
192
	if (rlen == -1 && errno == EINTR)
193
		goto again;
194
	if (rlen == -1 || rlen < 4*sizeof(u_int32_t))
195
		return (FALSE);
196
	xdrs->x_op = XDR_DECODE;
197
	XDR_SETPOS(xdrs, 0);
198
	if (! xdr_callmsg(xdrs, msg))
199
		return (FALSE);
200
	su->su_xid = msg->rm_xid;
201
	if (su->su_cache != NULL) {
202
		if (cache_get(xprt, msg, &reply, &replylen)) {
203
			(void) sendto(xprt->xp_sock, reply, (int) replylen, 0,
204
			    (struct sockaddr *) &xprt->xp_raddr,
205
			    xprt->xp_addrlen);
206
			return (TRUE);
207
		}
208
	}
209
	return (TRUE);
210
}
211
212
static bool_t
213
svcudp_reply(SVCXPRT *xprt, struct rpc_msg *msg)
214
{
215
	struct svcudp_data *su = su_data(xprt);
216
	XDR *xdrs = &(su->su_xdrs);
217
	int slen;
218
	bool_t stat = FALSE;
219
220
	xdrs->x_op = XDR_ENCODE;
221
	XDR_SETPOS(xdrs, 0);
222
	msg->rm_xid = su->su_xid;
223
	if (xdr_replymsg(xdrs, msg)) {
224
		slen = (int)XDR_GETPOS(xdrs);
225
		if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0,
226
		    (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen)
227
		    == slen) {
228
			stat = TRUE;
229
			if (su->su_cache && slen >= 0) {
230
				cache_set(xprt, (u_long) slen);
231
			}
232
		}
233
	}
234
	return (stat);
235
}
236
237
static bool_t
238
svcudp_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
239
{
240
241
	return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr));
242
}
243
244
static bool_t
245
svcudp_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
246
{
247
	XDR *xdrs = &(su_data(xprt)->su_xdrs);
248
249
	xdrs->x_op = XDR_FREE;
250
	return ((*xdr_args)(xdrs, args_ptr));
251
}
252
253
static void
254
svcudp_destroy(SVCXPRT *xprt)
255
{
256
	struct svcudp_data *su = su_data(xprt);
257
258
	xprt_unregister(xprt);
259
	if (xprt->xp_sock != -1)
260
		(void)close(xprt->xp_sock);
261
	xprt->xp_sock = -1;
262
	XDR_DESTROY(&(su->su_xdrs));
263
	mem_free(rpc_buffer(xprt), su->su_iosz);
264
	mem_free((caddr_t)su, sizeof(struct svcudp_data));
265
	mem_free((caddr_t)xprt, sizeof(SVCXPRT));
266
}
267
268
/*
269
 * Fifo cache for udp server
270
 * Copies pointers to reply buffers into fifo cache
271
 * Buffers are sent again if retransmissions are detected.
272
 */
273
274
#define SPARSENESS 4	/* 75% sparse */
275
276
/*
277
 * An entry in the cache
278
 */
279
typedef struct cache_node *cache_ptr;
280
struct cache_node {
281
	/*
282
	 * Index into cache is xid, proc, vers, prog and address
283
	 */
284
	u_long cache_xid;
285
	u_long cache_proc;
286
	u_long cache_vers;
287
	u_long cache_prog;
288
	struct sockaddr_in cache_addr;
289
	/*
290
	 * The cached reply and length
291
	 */
292
	char * cache_reply;
293
	u_long cache_replylen;
294
	/*
295
 	 * Next node on the list, if there is a collision
296
	 */
297
	cache_ptr cache_next;
298
};
299
300
/*
301
 * The entire cache
302
 */
303
struct udp_cache {
304
	u_long uc_size;		/* size of cache */
305
	cache_ptr *uc_entries;	/* hash table of entries in cache */
306
	cache_ptr *uc_fifo;	/* fifo list of entries in cache */
307
	u_long uc_nextvictim;	/* points to next victim in fifo list */
308
	u_long uc_prog;		/* saved program number */
309
	u_long uc_vers;		/* saved version number */
310
	u_long uc_proc;		/* saved procedure number */
311
	struct sockaddr_in uc_addr; /* saved caller's address */
312
};
313
314
315
/*
316
 * the hashing function
317
 */
318
#define CACHE_LOC(transp, xid)	\
319
 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
320
321
322
/*
323
 * Enable use of the cache.
324
 * Note: there is no disable.
325
 */
326
int
327
svcudp_enablecache(SVCXPRT *transp, u_long size)
328
{
329
	struct svcudp_data *su = su_data(transp);
330
	struct udp_cache *uc;
331
332
	if (su->su_cache != NULL)
333
		return(0);
334
	uc = malloc(sizeof(*uc));
335
	if (uc == NULL)
336
		return(0);
337
	uc->uc_size = size;
338
	uc->uc_nextvictim = 0;
339
	if (size > SIZE_MAX / (sizeof(cache_ptr) * SPARSENESS) ||
340
	    (uc->uc_entries = calloc(size, sizeof(cache_ptr) * SPARSENESS)) == NULL) {
341
		free(uc);
342
		return(0);
343
	}
344
	uc->uc_fifo = calloc(sizeof(cache_ptr), size);
345
	if (uc->uc_fifo == NULL) {
346
		free(uc->uc_entries);
347
		free(uc);
348
		return(0);
349
	}
350
	su->su_cache = (char *) uc;
351
	return(1);
352
}
353
354
355
/*
356
 * Set an entry in the cache
357
 */
358
static void
359
cache_set(SVCXPRT *xprt, u_long replylen)
360
{
361
	cache_ptr victim;
362
	cache_ptr *vicp;
363
	struct svcudp_data *su = su_data(xprt);
364
	struct udp_cache *uc = (struct udp_cache *) su->su_cache;
365
	u_int loc;
366
	char *newbuf;
367
368
	/*
369
 	 * Find space for the new entry, either by
370
	 * reusing an old entry, or by mallocing a new one
371
	 */
372
	victim = uc->uc_fifo[uc->uc_nextvictim];
373
	if (victim != NULL) {
374
		loc = CACHE_LOC(xprt, victim->cache_xid);
375
		for (vicp = &uc->uc_entries[loc];
376
		  *vicp != NULL && *vicp != victim;
377
		  vicp = &(*vicp)->cache_next)
378
				;
379
		if (*vicp == NULL) {
380
			return;
381
		}
382
		*vicp = victim->cache_next;	/* remote from cache */
383
		newbuf = victim->cache_reply;
384
	} else {
385
		victim = malloc(sizeof(struct cache_node));
386
		if (victim == NULL) {
387
			return;
388
		}
389
		newbuf = malloc(su->su_iosz);
390
		if (newbuf == NULL) {
391
			free(victim);
392
			return;
393
		}
394
	}
395
396
	/*
397
	 * Store it away
398
	 */
399
	victim->cache_replylen = replylen;
400
	victim->cache_reply = rpc_buffer(xprt);
401
	rpc_buffer(xprt) = newbuf;
402
	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE);
403
	victim->cache_xid = su->su_xid;
404
	victim->cache_proc = uc->uc_proc;
405
	victim->cache_vers = uc->uc_vers;
406
	victim->cache_prog = uc->uc_prog;
407
	victim->cache_addr = uc->uc_addr;
408
	loc = CACHE_LOC(xprt, victim->cache_xid);
409
	victim->cache_next = uc->uc_entries[loc];
410
	uc->uc_entries[loc] = victim;
411
	uc->uc_fifo[uc->uc_nextvictim++] = victim;
412
	uc->uc_nextvictim %= uc->uc_size;
413
}
414
415
/*
416
 * Try to get an entry from the cache
417
 * return 1 if found, 0 if not found
418
 */
419
static int
420
cache_get(SVCXPRT *xprt, struct rpc_msg *msg, char **replyp, u_long *replylenp)
421
{
422
	u_int loc;
423
	cache_ptr ent;
424
	struct svcudp_data *su = su_data(xprt);
425
	struct udp_cache *uc = (struct udp_cache *) su->su_cache;
426
427
#	define EQADDR(a1, a2)	(memcmp(&a1, &a2, sizeof(a1)) == 0)
428
429
	loc = CACHE_LOC(xprt, su->su_xid);
430
	for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
431
		if (ent->cache_xid == su->su_xid &&
432
		  ent->cache_proc == uc->uc_proc &&
433
		  ent->cache_vers == uc->uc_vers &&
434
		  ent->cache_prog == uc->uc_prog &&
435
		  EQADDR(ent->cache_addr, uc->uc_addr)) {
436
			*replyp = ent->cache_reply;
437
			*replylenp = ent->cache_replylen;
438
			return(1);
439
		}
440
	}
441
	/*
442
	 * Failed to find entry
443
	 * Remember a few things so we can do a set later
444
	 */
445
	uc->uc_proc = msg->rm_call.cb_proc;
446
	uc->uc_vers = msg->rm_call.cb_vers;
447
	uc->uc_prog = msg->rm_call.cb_prog;
448
	uc->uc_addr = xprt->xp_raddr;
449
	return(0);
450
}
451