1/*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
7 * Reserved.  This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.1 (the "License").  You may not use this file
10 * except in compliance with the License.  Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT.  Please see the
19 * License for the specific language governing rights and limitations
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24/*
25 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
26 * unrestricted use provided that this legend is included on all tape
27 * media and as a part of the software program in whole or part.  Users
28 * may copy or modify Sun RPC without charge, but are not authorized
29 * to license or distribute it to anyone else except as part of a product or
30 * program developed by the user.
31 *
32 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
33 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
34 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
35 *
36 * Sun RPC is provided with no support and without any obligation on the
37 * part of Sun Microsystems, Inc. to assist in its use, correction,
38 * modification or enhancement.
39 *
40 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
41 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
42 * OR ANY PART THEREOF.
43 *
44 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
45 * or profits or other special, indirect and consequential damages, even if
46 * Sun has been advised of the possibility of such damages.
47 *
48 * Sun Microsystems, Inc.
49 * 2550 Garcia Avenue
50 * Mountain View, California  94043
51 */
52
53#if defined(LIBC_SCCS) && !defined(lint)
54/*static char *sccsid = "from: @(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";*/
55/*static char *sccsid = "from: @(#)svc_udp.c	2.2 88/07/29 4.0 RPCSRC";*/
56static char *rcsid = "$Id: svc_udp.c,v 1.5 2004/10/13 00:24:07 jkh Exp $";
57#endif
58
59/*
60 * svc_udp.c,
61 * Server side for UDP/IP based RPC.  (Does some caching in the hopes of
62 * achieving execute-at-most-once semantics.)
63 *
64 * Copyright (C) 1984, Sun Microsystems, Inc.
65 */
66
67#include <stdio.h>
68#include <stdlib.h>
69#include <stdint.h>
70#include <string.h>
71#include <unistd.h>
72#include <rpc/rpc.h>
73#include <sys/socket.h>
74#include <sys/param.h>
75#include <errno.h>
76
77extern int		bindresvport();
78
79#define rpc_buffer(xprt) ((xprt)->xp_p1)
80
81static bool_t		svcudp_recv();
82static bool_t		svcudp_reply();
83static enum xprt_stat	svcudp_stat();
84static bool_t		svcudp_getargs();
85static bool_t		svcudp_freeargs();
86static void		svcudp_destroy();
87
88static struct xp_ops svcudp_op = {
89	svcudp_recv,
90	svcudp_stat,
91	svcudp_getargs,
92	svcudp_reply,
93	svcudp_freeargs,
94	svcudp_destroy
95};
96
97extern int errno;
98
99/*
100 * kept in xprt->xp_p2
101 */
102struct svcudp_data {
103	u_int   su_iosz;	/* byte size of send.recv buffer */
104#ifdef __LP64__
105	uint32_t	su_xid;		/* transaction id */
106#else
107	u_long	su_xid;		/* transaction id */
108#endif
109	XDR	su_xdrs;	/* XDR handle */
110	char	su_verfbody[MAX_AUTH_BYTES];	/* verifier body */
111	char * 	su_cache;	/* cached data, NULL if no cache */
112};
113#define	su_data(xprt)	((struct svcudp_data *)(xprt->xp_p2))
114
115/*
116 * Usage:
117 *	xprt = svcudp_create(sock);
118 *
119 * If sock<0 then a socket is created, else sock is used.
120 * If the socket, sock is not bound to a port then svcudp_create
121 * binds it to an arbitrary port.  In any (successful) case,
122 * xprt->xp_sock is the registered socket number and xprt->xp_port is the
123 * associated port number.
124 * Once *xprt is initialized, it is registered as a transporter;
125 * see (svc.h, xprt_register).
126 * The routines returns NULL if a problem occurred.
127 */
128SVCXPRT *
129svcudp_bufcreate(sock, sendsz, recvsz)
130	register int sock;
131	u_int sendsz, recvsz;
132{
133	bool_t madesock = FALSE;
134	register SVCXPRT *xprt;
135	register struct svcudp_data *su;
136	struct sockaddr_in addr;
137	unsigned int len = sizeof(struct sockaddr_in);
138
139	if (sock == RPC_ANYSOCK) {
140		if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
141			perror("svcudp_create: socket creation problem");
142			return ((SVCXPRT *)NULL);
143		}
144		madesock = TRUE;
145	}
146	bzero((char *)&addr, sizeof (addr));
147	addr.sin_family = AF_INET;
148	if (bindresvport(sock, &addr)) {
149		addr.sin_port = 0;
150		(void)bind(sock, (struct sockaddr *)&addr, len);
151	}
152	if (getsockname(sock, (struct sockaddr *)&addr, &len) != 0) {
153		perror("svcudp_create - cannot getsockname");
154		if (madesock)
155			(void)close(sock);
156		return ((SVCXPRT *)NULL);
157	}
158	xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
159	if (xprt == NULL) {
160		(void)fprintf(stderr, "svcudp_create: out of memory\n");
161		return (NULL);
162	}
163	su = (struct svcudp_data *)mem_alloc(sizeof(*su));
164	if (su == NULL) {
165		(void)fprintf(stderr, "svcudp_create: out of memory\n");
166		return (NULL);
167	}
168	su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4;
169	if ((rpc_buffer(xprt) = mem_alloc(su->su_iosz)) == NULL) {
170		(void)fprintf(stderr, "svcudp_create: out of memory\n");
171		return (NULL);
172	}
173	xdrmem_create(
174	    &(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE);
175	su->su_cache = NULL;
176	xprt->xp_p2 = (caddr_t)su;
177	xprt->xp_verf.oa_base = su->su_verfbody;
178	xprt->xp_ops = &svcudp_op;
179	xprt->xp_port = ntohs(addr.sin_port);
180	xprt->xp_sock = sock;
181	xprt_register(xprt);
182	return (xprt);
183}
184
185SVCXPRT *
186svcudp_create(sock)
187	int sock;
188{
189
190	return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE));
191}
192
193static enum xprt_stat
194svcudp_stat(xprt)
195	SVCXPRT *xprt;
196{
197
198	return (XPRT_IDLE);
199}
200
201static int cache_get();
202static void cache_set();
203
204static bool_t
205svcudp_recv(xprt, msg)
206	register SVCXPRT *xprt;
207	struct rpc_msg *msg;
208{
209	register struct svcudp_data *su = su_data(xprt);
210	register XDR *xdrs = &(su->su_xdrs);
211	register int rlen;
212	char *reply;
213#ifdef __LP64__
214	uint32_t replylen;
215#else
216	u_long replylen;
217#endif
218
219    again:
220	xprt->xp_addrlen = sizeof(struct sockaddr_in);
221	rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, 0, (struct sockaddr *)&(xprt->xp_raddr), (unsigned int *)&(xprt->xp_addrlen));
222	if (rlen == -1 && errno == EINTR)
223		goto again;
224#ifdef __LP64__
225	if (rlen < 4*sizeof(uint32_t))
226		return (FALSE);
227#else
228	if (rlen < 4*sizeof(u_long))
229		return (FALSE);
230#endif
231	xdrs->x_op = XDR_DECODE;
232	XDR_SETPOS(xdrs, 0);
233	if (! xdr_callmsg(xdrs, msg))
234		return (FALSE);
235	su->su_xid = msg->rm_xid;
236	if (su->su_cache != NULL) {
237		if (cache_get(xprt, msg, &reply, &replylen)) {
238			(void) sendto(xprt->xp_sock, reply, (int) replylen, 0,
239			  (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen);
240			return (TRUE);
241		}
242	}
243	return (TRUE);
244}
245
246static bool_t
247svcudp_reply(xprt, msg)
248	register SVCXPRT *xprt;
249	struct rpc_msg *msg;
250{
251	register struct svcudp_data *su = su_data(xprt);
252	register XDR *xdrs = &(su->su_xdrs);
253	register int slen;
254	register bool_t stat = FALSE;
255
256	xdrs->x_op = XDR_ENCODE;
257	XDR_SETPOS(xdrs, 0);
258	msg->rm_xid = su->su_xid;
259	if (xdr_replymsg(xdrs, msg)) {
260		slen = (int)XDR_GETPOS(xdrs);
261		if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0,
262		    (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen)
263		    == slen) {
264			stat = TRUE;
265			if (su->su_cache && slen >= 0) {
266#ifdef __LP64__
267				cache_set(xprt, (uint32_t) slen);
268#else
269				cache_set(xprt, (u_long) slen);
270#endif
271			}
272		}
273	}
274	return (stat);
275}
276
277static bool_t
278svcudp_getargs(xprt, xdr_args, args_ptr)
279	SVCXPRT *xprt;
280	xdrproc_t xdr_args;
281	caddr_t args_ptr;
282{
283
284	return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr, 0));
285}
286
287static bool_t
288svcudp_freeargs(xprt, xdr_args, args_ptr)
289	SVCXPRT *xprt;
290	xdrproc_t xdr_args;
291	caddr_t args_ptr;
292{
293	register XDR *xdrs = &(su_data(xprt)->su_xdrs);
294
295	xdrs->x_op = XDR_FREE;
296	return ((*xdr_args)(xdrs, args_ptr, 0));
297}
298
299static void
300svcudp_destroy(xprt)
301	register SVCXPRT *xprt;
302{
303	register struct svcudp_data *su = su_data(xprt);
304
305	xprt_unregister(xprt);
306	(void)close(xprt->xp_sock);
307	XDR_DESTROY(&(su->su_xdrs));
308	mem_free(rpc_buffer(xprt), su->su_iosz);
309	mem_free((caddr_t)su, sizeof(struct svcudp_data));
310	mem_free((caddr_t)xprt, sizeof(SVCXPRT));
311}
312
313
314/***********this could be a separate file*********************/
315
316/*
317 * Fifo cache for udp server
318 * Copies pointers to reply buffers into fifo cache
319 * Buffers are sent again if retransmissions are detected.
320 */
321
322#define SPARSENESS 4	/* 75% sparse */
323
324#define CACHE_PERROR(msg)	\
325	(void) fprintf(stderr,"%s\n", msg)
326
327#define ALLOC(type, size)	\
328	(type *) mem_alloc((unsigned) (sizeof(type) * (size)))
329
330#define BZERO(addr, type, size)	 \
331	bzero((char *) addr, sizeof(type) * (int) (size))
332
333/*
334 * An entry in the cache
335 */
336typedef struct cache_node *cache_ptr;
337struct cache_node {
338	/*
339	 * Index into cache is xid, proc, vers, prog and address
340	 */
341#ifdef __LP64__
342	uint32_t cache_xid;
343	uint32_t cache_proc;
344	uint32_t cache_vers;
345	uint32_t cache_prog;
346#else
347	u_long cache_xid;
348	u_long cache_proc;
349	u_long cache_vers;
350	u_long cache_prog;
351#endif
352	struct sockaddr_in cache_addr;
353	/*
354	 * The cached reply and length
355	 */
356	char * cache_reply;
357#ifdef __LP64__
358	uint32_t cache_replylen;
359#else
360	u_long cache_replylen;
361#endif
362	/*
363 	 * Next node on the list, if there is a collision
364	 */
365	cache_ptr cache_next;
366};
367
368
369
370/*
371 * The entire cache
372 */
373struct udp_cache {
374#ifdef __LP64__
375	uint32_t uc_size;		/* size of cache */
376#else
377	u_long uc_size;		/* size of cache */
378#endif
379	cache_ptr *uc_entries;	/* hash table of entries in cache */
380	cache_ptr *uc_fifo;	/* fifo list of entries in cache */
381#ifdef __LP64__
382	uint32_t uc_nextvictim;	/* points to next victim in fifo list */
383	uint32_t uc_prog;		/* saved program number */
384	uint32_t uc_vers;		/* saved version number */
385	uint32_t uc_proc;		/* saved procedure number */
386#else
387	u_long uc_nextvictim;	/* points to next victim in fifo list */
388	u_long uc_prog;		/* saved program number */
389	u_long uc_vers;		/* saved version number */
390	u_long uc_proc;		/* saved procedure number */
391#endif
392	struct sockaddr_in uc_addr; /* saved caller's address */
393};
394
395
396/*
397 * the hashing function
398 */
399#define CACHE_LOC(transp, xid)	\
400 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
401
402
403/*
404 * Enable use of the cache.
405 * Note: there is no disable.
406 */
407int
408svcudp_enablecache(transp, size)
409	SVCXPRT *transp;
410#ifdef __LP64__
411	uint32_t size;
412#else
413	u_long size;
414#endif
415{
416	struct svcudp_data *su = su_data(transp);
417	struct udp_cache *uc;
418
419	if (su->su_cache != NULL) {
420		CACHE_PERROR("enablecache: cache already enabled");
421		return(0);
422	}
423	uc = ALLOC(struct udp_cache, 1);
424	if (uc == NULL) {
425		CACHE_PERROR("enablecache: could not allocate cache");
426		return(0);
427	}
428	uc->uc_size = size;
429	uc->uc_nextvictim = 0;
430	uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS);
431	if (uc->uc_entries == NULL) {
432		CACHE_PERROR("enablecache: could not allocate cache data");
433		return(0);
434	}
435	BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS);
436	uc->uc_fifo = ALLOC(cache_ptr, size);
437	if (uc->uc_fifo == NULL) {
438		CACHE_PERROR("enablecache: could not allocate cache fifo");
439		return(0);
440	}
441	BZERO(uc->uc_fifo, cache_ptr, size);
442	su->su_cache = (char *) uc;
443	return(1);
444}
445
446
447/*
448 * Set an entry in the cache
449 */
450static void
451cache_set(xprt, replylen)
452	SVCXPRT *xprt;
453#ifdef __LP64__
454	uint32_t replylen;
455#else
456	u_long replylen;
457#endif
458{
459	register cache_ptr victim;
460	register cache_ptr *vicp;
461	register struct svcudp_data *su = su_data(xprt);
462	struct udp_cache *uc = (struct udp_cache *) su->su_cache;
463	u_int loc;
464	char *newbuf;
465
466	/*
467 	 * Find space for the new entry, either by
468	 * reusing an old entry, or by mallocing a new one
469	 */
470	victim = uc->uc_fifo[uc->uc_nextvictim];
471	if (victim != NULL) {
472		loc = CACHE_LOC(xprt, victim->cache_xid);
473		for (vicp = &uc->uc_entries[loc];
474		  *vicp != NULL && *vicp != victim;
475		  vicp = &(*vicp)->cache_next)
476				;
477		if (*vicp == NULL) {
478			CACHE_PERROR("cache_set: victim not found");
479			return;
480		}
481		*vicp = victim->cache_next;	/* remote from cache */
482		newbuf = victim->cache_reply;
483	} else {
484		victim = ALLOC(struct cache_node, 1);
485		if (victim == NULL) {
486			CACHE_PERROR("cache_set: victim alloc failed");
487			return;
488		}
489		newbuf = mem_alloc(su->su_iosz);
490		if (newbuf == NULL) {
491			CACHE_PERROR("cache_set: could not allocate new rpc_buffer");
492			return;
493		}
494	}
495
496	/*
497	 * Store it away
498	 */
499	victim->cache_replylen = replylen;
500	victim->cache_reply = rpc_buffer(xprt);
501	rpc_buffer(xprt) = newbuf;
502	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE);
503	victim->cache_xid = su->su_xid;
504	victim->cache_proc = uc->uc_proc;
505	victim->cache_vers = uc->uc_vers;
506	victim->cache_prog = uc->uc_prog;
507	victim->cache_addr = uc->uc_addr;
508	loc = CACHE_LOC(xprt, victim->cache_xid);
509	victim->cache_next = uc->uc_entries[loc];
510	uc->uc_entries[loc] = victim;
511	uc->uc_fifo[uc->uc_nextvictim++] = victim;
512	uc->uc_nextvictim %= uc->uc_size;
513}
514
515/*
516 * Try to get an entry from the cache
517 * return 1 if found, 0 if not found
518 */
519static int
520cache_get(xprt, msg, replyp, replylenp)
521	SVCXPRT *xprt;
522	struct rpc_msg *msg;
523	char **replyp;
524#ifdef __LP64__
525	uint32_t *replylenp;
526#else
527	u_long *replylenp;
528#endif
529{
530	u_int loc;
531	register cache_ptr ent;
532	register struct svcudp_data *su = su_data(xprt);
533	register struct udp_cache *uc = (struct udp_cache *) su->su_cache;
534
535#	define EQADDR(a1, a2)	(bcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
536
537	loc = CACHE_LOC(xprt, su->su_xid);
538	for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
539		if (ent->cache_xid == su->su_xid &&
540		  ent->cache_proc == uc->uc_proc &&
541		  ent->cache_vers == uc->uc_vers &&
542		  ent->cache_prog == uc->uc_prog &&
543		  EQADDR(ent->cache_addr, uc->uc_addr)) {
544			*replyp = ent->cache_reply;
545			*replylenp = ent->cache_replylen;
546			return(1);
547		}
548	}
549	/*
550	 * Failed to find entry
551	 * Remember a few things so we can do a set later
552	 */
553	uc->uc_proc = msg->rm_call.cb_proc;
554	uc->uc_vers = msg->rm_call.cb_vers;
555	uc->uc_prog = msg->rm_call.cb_prog;
556	uc->uc_addr = xprt->xp_raddr;
557	return(0);
558}
559
560