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