xref: /freebsd/crypto/krb5/src/lib/rpc/svc_udp.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* @(#)svc_udp.c	2.2 88/07/29 4.0 RPCSRC */
2 /*
3  * Copyright (c) 2010, Oracle America, Inc.
4  *
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *
18  *     * Neither the name of the "Oracle America, Inc." nor the names of
19  *       its contributors may be used to endorse or promote products
20  *       derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
23  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 #if !defined(lint) && defined(SCCSIDS)
35 static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";
36 #endif
37 
38 /*
39  * svc_udp.c,
40  * Server side for UDP/IP based RPC.  (Does some caching in the hopes of
41  * achieving execute-at-most-once semantics.)
42  */
43 
44 #include "k5-platform.h"
45 #include <unistd.h>
46 #include <gssrpc/rpc.h>
47 #include <sys/socket.h>
48 #ifdef HAVE_SYS_UIO_H
49 #include <sys/uio.h>
50 #endif
51 #include <port-sockets.h>
52 #include <socket-utils.h>
53 
54 
55 #ifndef GETSOCKNAME_ARG3_TYPE
56 #define GETSOCKNAME_ARG3_TYPE int
57 #endif
58 
59 #define rpc_buffer(xprt) ((xprt)->xp_p1)
60 #ifndef MAX
61 #define MAX(a, b)     ((a > b) ? a : b)
62 #endif
63 
64 static bool_t		svcudp_recv(SVCXPRT *, struct rpc_msg *);
65 static bool_t		svcudp_reply(SVCXPRT *, struct rpc_msg *);
66 static enum xprt_stat	svcudp_stat(SVCXPRT *);
67 static bool_t		svcudp_getargs(SVCXPRT *, xdrproc_t, void *);
68 static bool_t		svcudp_freeargs(SVCXPRT *, xdrproc_t, void *);
69 static void		svcudp_destroy(SVCXPRT *);
70 
71 static void cache_set(SVCXPRT *, uint32_t);
72 static int cache_get(SVCXPRT *, struct rpc_msg *, char **, uint32_t *);
73 
74 static struct xp_ops svcudp_op = {
75 	svcudp_recv,
76 	svcudp_stat,
77 	svcudp_getargs,
78 	svcudp_reply,
79 	svcudp_freeargs,
80 	svcudp_destroy
81 };
82 
83 
84 /*
85  * kept in xprt->xp_p2
86  */
87 struct svcudp_data {
88 	u_int   su_iosz;	/* byte size of send.recv buffer */
89 	uint32_t	su_xid;		/* transaction id */
90 	XDR	su_xdrs;	/* XDR handle */
91 	char	su_verfbody[MAX_AUTH_BYTES];	/* verifier body */
92 	void * 	su_cache;	/* cached data, NULL if no cache */
93 };
94 #define	su_data(xprt)	((struct svcudp_data *)(xprt->xp_p2))
95 
96 /*
97  * Usage:
98  *	xprt = svcudp_create(sock);
99  *
100  * If sock<0 then a socket is created, else sock is used.
101  * If the socket, sock is not bound to a port then svcudp_create
102  * binds it to an arbitrary port.  In any (successful) case,
103  * xprt->xp_sock is the registered socket number and xprt->xp_port is the
104  * associated port number.
105  * Once *xprt is initialized, it is registered as a transporter;
106  * see (svc.h, xprt_register).
107  * The routines returns NULL if a problem occurred.
108  */
109 SVCXPRT *
svcudp_bufcreate(int sock,u_int sendsz,u_int recvsz)110 svcudp_bufcreate(
111 	int sock,
112 	u_int sendsz,
113 	u_int recvsz)
114 {
115 	bool_t madesock = FALSE;
116 	SVCXPRT *xprt;
117 	struct svcudp_data *su;
118 	struct sockaddr_storage ss;
119 	struct sockaddr *sa = (struct sockaddr *)&ss;
120 	socklen_t len;
121 
122 	if (sock == RPC_ANYSOCK) {
123 		if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
124 			perror("svcudp_create: socket creation problem");
125 			return ((SVCXPRT *)NULL);
126 		}
127 		set_cloexec_fd(sock);
128 		madesock = TRUE;
129 		memset(&ss, 0, sizeof(ss));
130 		sa->sa_family = AF_INET;
131 	} else {
132 		len = sizeof(struct sockaddr_storage);
133 		if (getsockname(sock, sa, &len) < 0) {
134 			perror("svcudp_create - cannot getsockname");
135 			return ((SVCXPRT *)NULL);
136 		}
137 	}
138 
139 	if (bindresvport_sa(sock, sa)) {
140 		sa_setport(sa, 0);
141 		(void)bind(sock, sa, sa_socklen(sa));
142 	}
143 	len = sizeof(struct sockaddr_storage);
144 	if (getsockname(sock, sa, &len) != 0) {
145 		perror("svcudp_create - cannot getsockname");
146 		if (madesock)
147 			(void)close(sock);
148 		return ((SVCXPRT *)NULL);
149 	}
150 	xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
151 	if (xprt == NULL) {
152 		(void)fprintf(stderr, "svcudp_create: out of memory\n");
153 		return (NULL);
154 	}
155 	su = (struct svcudp_data *)mem_alloc(sizeof(*su));
156 	if (su == NULL) {
157 		(void)fprintf(stderr, "svcudp_create: out of memory\n");
158 		return (NULL);
159 	}
160 	su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4;
161 	if ((rpc_buffer(xprt) = mem_alloc(su->su_iosz)) == NULL) {
162 		(void)fprintf(stderr, "svcudp_create: out of memory\n");
163 		return (NULL);
164 	}
165 	xdrmem_create(
166 	    &(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE);
167 	su->su_cache = NULL;
168 	xprt->xp_p2 = (caddr_t)su;
169 	xprt->xp_auth = NULL;
170 	xprt->xp_verf.oa_base = su->su_verfbody;
171 	xprt->xp_ops = &svcudp_op;
172 	xprt->xp_port = sa_getport(sa);
173 	xprt->xp_sock = sock;
174 	xprt_register(xprt);
175 	return (xprt);
176 }
177 
178 SVCXPRT *
svcudp_create(int sock)179 svcudp_create(int sock)
180 {
181 
182 	return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE));
183 }
184 
185 static enum xprt_stat
svcudp_stat(SVCXPRT * xprt)186 svcudp_stat(SVCXPRT *xprt)
187 {
188 
189 	return (XPRT_IDLE);
190 }
191 
192 static bool_t
svcudp_recv(SVCXPRT * xprt,struct rpc_msg * msg)193 svcudp_recv(
194 	SVCXPRT *xprt,
195 	struct rpc_msg *msg)
196 {
197         struct msghdr dummy;
198 	struct iovec dummy_iov[1];
199 	struct svcudp_data *su = su_data(xprt);
200 	XDR *xdrs = &su->su_xdrs;
201 	int rlen;
202 	char *reply;
203 	uint32_t replylen;
204 	socklen_t addrlen;
205 
206     again:
207 	memset(&dummy, 0, sizeof(dummy));
208 	dummy_iov[0].iov_base = rpc_buffer(xprt);
209 	dummy_iov[0].iov_len = (int) su->su_iosz;
210 	dummy.msg_iov = dummy_iov;
211 	dummy.msg_iovlen = 1;
212 	dummy.msg_namelen = xprt->xp_laddrlen = sizeof(struct sockaddr_in);
213 	dummy.msg_name = (char *) &xprt->xp_laddr;
214 	rlen = recvmsg(xprt->xp_sock, &dummy, MSG_PEEK);
215 	if (rlen == -1) {
216 	     if (errno == EINTR)
217 		  goto again;
218 	     else
219 		  return (FALSE);
220 	}
221 
222 	addrlen = sizeof(struct sockaddr_in);
223 	rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz,
224 	    0, (struct sockaddr *)&(xprt->xp_raddr), &addrlen);
225 	if (rlen == -1 && errno == EINTR)
226 		goto again;
227 	if (rlen < (int) (4*sizeof(uint32_t)))
228 		return (FALSE);
229 	xprt->xp_addrlen = addrlen;
230 	xdrs->x_op = XDR_DECODE;
231 	XDR_SETPOS(xdrs, 0);
232 	if (! xdr_callmsg(xdrs, msg))
233 		return (FALSE);
234 	su->su_xid = msg->rm_xid;
235 	if (su->su_cache != NULL) {
236 		if (cache_get(xprt, msg, &reply, &replylen)) {
237 			(void) sendto(xprt->xp_sock, reply, (int) replylen, 0,
238 			  (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen);
239 			return (TRUE);
240 		}
241 	}
242 	return (TRUE);
243 }
244 
svcudp_reply(SVCXPRT * xprt,struct rpc_msg * msg)245 static bool_t svcudp_reply(
246 	SVCXPRT *xprt,
247 	struct rpc_msg *msg)
248 {
249      struct svcudp_data *su = su_data(xprt);
250      XDR *xdrs = &su->su_xdrs;
251      int slen;
252      bool_t stat = FALSE;
253 
254      xdrproc_t xdr_results = NULL;
255      caddr_t xdr_location = 0;
256      bool_t has_args;
257 
258      if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
259 	 msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
260 	  has_args = TRUE;
261 	  xdr_results = msg->acpted_rply.ar_results.proc;
262 	  xdr_location = msg->acpted_rply.ar_results.where;
263 
264 	  msg->acpted_rply.ar_results.proc = xdr_void;
265 	  msg->acpted_rply.ar_results.where = NULL;
266      } else
267 	  has_args = FALSE;
268 
269      xdrs->x_op = XDR_ENCODE;
270      XDR_SETPOS(xdrs, 0);
271      msg->rm_xid = su->su_xid;
272      if (xdr_replymsg(xdrs, msg) &&
273 	 (!has_args ||
274 	  (SVCAUTH_WRAP(xprt->xp_auth, xdrs, xdr_results, xdr_location)))) {
275 	  slen = (int)XDR_GETPOS(xdrs);
276 	  if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0,
277 		     (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen)
278 	      == slen) {
279 	       stat = TRUE;
280 	       if (su->su_cache && slen >= 0) {
281 		    cache_set(xprt, (uint32_t) slen);
282 	       }
283 	  }
284      }
285      return (stat);
286 }
287 
288 static bool_t
svcudp_getargs(SVCXPRT * xprt,xdrproc_t xdr_args,void * args_ptr)289 svcudp_getargs(
290 	SVCXPRT *xprt,
291 	xdrproc_t xdr_args,
292 	void * args_ptr)
293 {
294 	if (! SVCAUTH_UNWRAP(xprt->xp_auth, &(su_data(xprt)->su_xdrs),
295 			     xdr_args, args_ptr)) {
296 		(void)svcudp_freeargs(xprt, xdr_args, args_ptr);
297 		return FALSE;
298 	}
299 	return TRUE;
300 }
301 
302 static bool_t
svcudp_freeargs(SVCXPRT * xprt,xdrproc_t xdr_args,void * args_ptr)303 svcudp_freeargs(
304 	SVCXPRT *xprt,
305 	xdrproc_t xdr_args,
306 	void * args_ptr)
307 {
308 	XDR *xdrs = &su_data(xprt)->su_xdrs;
309 
310 	xdrs->x_op = XDR_FREE;
311 	return ((*xdr_args)(xdrs, args_ptr));
312 }
313 
314 static void
svcudp_destroy(SVCXPRT * xprt)315 svcudp_destroy(SVCXPRT *xprt)
316 {
317 	struct svcudp_data *su = su_data(xprt);
318 
319 	xprt_unregister(xprt);
320         if (xprt->xp_sock != INVALID_SOCKET)
321                 (void)closesocket(xprt->xp_sock);
322         xprt->xp_sock = INVALID_SOCKET;
323 	if (xprt->xp_auth != NULL) {
324 		SVCAUTH_DESTROY(xprt->xp_auth);
325 		xprt->xp_auth = NULL;
326 	}
327 	XDR_DESTROY(&(su->su_xdrs));
328 	mem_free(rpc_buffer(xprt), su->su_iosz);
329 	mem_free((caddr_t)su, sizeof(struct svcudp_data));
330 	mem_free((caddr_t)xprt, sizeof(SVCXPRT));
331 }
332 
333 
334 /***********this could be a separate file*********************/
335 
336 /*
337  * Fifo cache for udp server
338  * Copies pointers to reply buffers into fifo cache
339  * Buffers are sent again if retransmissions are detected.
340  */
341 
342 #define SPARSENESS 4	/* 75% sparse */
343 
344 #define CACHE_PERROR(msg)	\
345 	(void) fprintf(stderr,"%s\n", msg)
346 
347 #define ALLOC(type, size)	\
348 	(type *) mem_alloc((unsigned) (sizeof(type) * (size)))
349 
350 #define BZERO(addr, type, size)	 \
351 	memset(addr, 0, sizeof(type) * (int) (size))
352 
353 /*
354  * An entry in the cache
355  */
356 typedef struct cache_node *cache_ptr;
357 struct cache_node {
358 	/*
359 	 * Index into cache is xid, proc, vers, prog and address
360 	 */
361 	uint32_t cache_xid;
362 	rpcproc_t cache_proc;
363 	rpcvers_t cache_vers;
364 	rpcprog_t cache_prog;
365 	struct sockaddr_in cache_addr;
366 	/*
367 	 * The cached reply and length
368 	 */
369 	char * cache_reply;
370 	uint32_t cache_replylen;
371 	/*
372  	 * Next node on the list, if there is a collision
373 	 */
374 	cache_ptr cache_next;
375 };
376 
377 
378 
379 /*
380  * The entire cache
381  */
382 struct udp_cache {
383 	uint32_t uc_size;		/* size of cache */
384 	cache_ptr *uc_entries;	/* hash table of entries in cache */
385 	cache_ptr *uc_fifo;	/* fifo list of entries in cache */
386 	uint32_t uc_nextvictim;	/* points to next victim in fifo list */
387 	rpcprog_t uc_prog;		/* saved program number */
388 	rpcvers_t uc_vers;		/* saved version number */
389 	rpcproc_t uc_proc;		/* saved procedure number */
390 	struct sockaddr_in uc_addr; /* saved caller's address */
391 };
392 
393 
394 /*
395  * the hashing function
396  */
397 #define CACHE_LOC(transp, xid)	\
398  (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
399 
400 
401 /*
402  * Enable use of the cache.
403  * Note: there is no disable.
404  */
405 int
svcudp_enablecache(SVCXPRT * transp,uint32_t size)406 svcudp_enablecache(
407 	SVCXPRT *transp,
408 	uint32_t size)
409 {
410 	struct svcudp_data *su = su_data(transp);
411 	struct udp_cache *uc;
412 
413 	if (su->su_cache != NULL) {
414 		CACHE_PERROR("enablecache: cache already enabled");
415 		return(0);
416 	}
417 	uc = ALLOC(struct udp_cache, 1);
418 	if (uc == NULL) {
419 		CACHE_PERROR("enablecache: could not allocate cache");
420 		return(0);
421 	}
422 	uc->uc_size = size;
423 	uc->uc_nextvictim = 0;
424 	uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS);
425 	if (uc->uc_entries == NULL) {
426 		CACHE_PERROR("enablecache: could not allocate cache data");
427 		return(0);
428 	}
429 	BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS);
430 	uc->uc_fifo = ALLOC(cache_ptr, size);
431 	if (uc->uc_fifo == NULL) {
432 		CACHE_PERROR("enablecache: could not allocate cache fifo");
433 		return(0);
434 	}
435 	BZERO(uc->uc_fifo, cache_ptr, size);
436 	su->su_cache = (char *) uc;
437 	return(1);
438 }
439 
440 
441 /*
442  * Set an entry in the cache
443  */
444 static void
cache_set(SVCXPRT * xprt,uint32_t replylen)445 cache_set(
446 	SVCXPRT *xprt,
447 	uint32_t replylen)
448 {
449 	cache_ptr victim;
450 	cache_ptr *vicp;
451 	struct svcudp_data *su = su_data(xprt);
452 	struct udp_cache *uc = (struct udp_cache *) su->su_cache;
453 	u_int loc;
454 	char *newbuf;
455 
456 	/*
457  	 * Find space for the new entry, either by
458 	 * reusing an old entry, or by mallocing a new one
459 	 */
460 	victim = uc->uc_fifo[uc->uc_nextvictim];
461 	if (victim != NULL) {
462 		loc = CACHE_LOC(xprt, victim->cache_xid);
463 		for (vicp = &uc->uc_entries[loc];
464 		  *vicp != NULL && *vicp != victim;
465 		  vicp = &(*vicp)->cache_next)
466 				;
467 		if (*vicp == NULL) {
468 			CACHE_PERROR("cache_set: victim not found");
469 			return;
470 		}
471 		*vicp = victim->cache_next;	/* remote from cache */
472 		newbuf = victim->cache_reply;
473 	} else {
474 		victim = ALLOC(struct cache_node, 1);
475 		if (victim == NULL) {
476 			CACHE_PERROR("cache_set: victim alloc failed");
477 			return;
478 		}
479 		newbuf = mem_alloc(su->su_iosz);
480 		if (newbuf == NULL) {
481 			CACHE_PERROR("cache_set: could not allocate new rpc_buffer");
482 			free(victim);
483 			return;
484 		}
485 	}
486 
487 	/*
488 	 * Store it away
489 	 */
490 	victim->cache_replylen = replylen;
491 	victim->cache_reply = rpc_buffer(xprt);
492 	rpc_buffer(xprt) = newbuf;
493 	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE);
494 	victim->cache_xid = su->su_xid;
495 	victim->cache_proc = uc->uc_proc;
496 	victim->cache_vers = uc->uc_vers;
497 	victim->cache_prog = uc->uc_prog;
498 	victim->cache_addr = uc->uc_addr;
499 	loc = CACHE_LOC(xprt, victim->cache_xid);
500 	victim->cache_next = uc->uc_entries[loc];
501 	uc->uc_entries[loc] = victim;
502 	uc->uc_fifo[uc->uc_nextvictim++] = victim;
503 	uc->uc_nextvictim %= uc->uc_size;
504 }
505 
506 /*
507  * Try to get an entry from the cache
508  * return 1 if found, 0 if not found
509  */
510 static int
cache_get(SVCXPRT * xprt,struct rpc_msg * msg,char ** replyp,uint32_t * replylenp)511 cache_get(
512 	SVCXPRT *xprt,
513 	struct rpc_msg *msg,
514 	char **replyp,
515 	uint32_t *replylenp)
516 {
517 	u_int loc;
518 	cache_ptr ent;
519 	struct svcudp_data *su = su_data(xprt);
520 	struct udp_cache *uc = su->su_cache;
521 
522 #	define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
523 
524 	loc = CACHE_LOC(xprt, su->su_xid);
525 	for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
526 		if (ent->cache_xid == su->su_xid &&
527 		  ent->cache_proc == uc->uc_proc &&
528 		  ent->cache_vers == uc->uc_vers &&
529 		  ent->cache_prog == uc->uc_prog &&
530 		  EQADDR(ent->cache_addr, uc->uc_addr)) {
531 			*replyp = ent->cache_reply;
532 			*replylenp = ent->cache_replylen;
533 			return(1);
534 		}
535 	}
536 	/*
537 	 * Failed to find entry
538 	 * Remember a few things so we can do a set later
539 	 */
540 	uc->uc_proc = msg->rm_call.cb_proc;
541 	uc->uc_vers = msg->rm_call.cb_vers;
542 	uc->uc_prog = msg->rm_call.cb_prog;
543 	uc->uc_addr = xprt->xp_raddr;
544 	return(0);
545 }
546