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