xref: /freebsd/crypto/krb5/src/lib/rpc/svc_udp.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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      u_int slen;
252      bool_t stat = FALSE;
253      ssize_t r;
254 
255      xdrproc_t xdr_results = NULL;
256      caddr_t xdr_location = 0;
257      bool_t has_args;
258 
259      if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
260 	 msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
261 	  has_args = TRUE;
262 	  xdr_results = msg->acpted_rply.ar_results.proc;
263 	  xdr_location = msg->acpted_rply.ar_results.where;
264 
265 	  msg->acpted_rply.ar_results.proc = xdr_void;
266 	  msg->acpted_rply.ar_results.where = NULL;
267      } else
268 	  has_args = FALSE;
269 
270      xdrs->x_op = XDR_ENCODE;
271      XDR_SETPOS(xdrs, 0);
272      msg->rm_xid = su->su_xid;
273      if (xdr_replymsg(xdrs, msg) &&
274 	 (!has_args ||
275 	  (SVCAUTH_WRAP(xprt->xp_auth, xdrs, xdr_results, xdr_location)))) {
276 	  slen = XDR_GETPOS(xdrs);
277 	  r = sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0,
278 		     (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen);
279 	  if (r >= 0 && (u_int)r == slen) {
280 	       stat = TRUE;
281 	       if (su->su_cache) {
282 		    cache_set(xprt, (uint32_t) slen);
283 	       }
284 	  }
285      }
286      return (stat);
287 }
288 
289 static bool_t
svcudp_getargs(SVCXPRT * xprt,xdrproc_t xdr_args,void * args_ptr)290 svcudp_getargs(
291 	SVCXPRT *xprt,
292 	xdrproc_t xdr_args,
293 	void * args_ptr)
294 {
295 	if (! SVCAUTH_UNWRAP(xprt->xp_auth, &(su_data(xprt)->su_xdrs),
296 			     xdr_args, args_ptr)) {
297 		(void)svcudp_freeargs(xprt, xdr_args, args_ptr);
298 		return FALSE;
299 	}
300 	return TRUE;
301 }
302 
303 static bool_t
svcudp_freeargs(SVCXPRT * xprt,xdrproc_t xdr_args,void * args_ptr)304 svcudp_freeargs(
305 	SVCXPRT *xprt,
306 	xdrproc_t xdr_args,
307 	void * args_ptr)
308 {
309 	XDR *xdrs = &su_data(xprt)->su_xdrs;
310 
311 	xdrs->x_op = XDR_FREE;
312 	return ((*xdr_args)(xdrs, args_ptr));
313 }
314 
315 static void
svcudp_destroy(SVCXPRT * xprt)316 svcudp_destroy(SVCXPRT *xprt)
317 {
318 	struct svcudp_data *su = su_data(xprt);
319 
320 	xprt_unregister(xprt);
321         if (xprt->xp_sock != INVALID_SOCKET)
322                 (void)closesocket(xprt->xp_sock);
323         xprt->xp_sock = INVALID_SOCKET;
324 	if (xprt->xp_auth != NULL) {
325 		SVCAUTH_DESTROY(xprt->xp_auth);
326 		xprt->xp_auth = NULL;
327 	}
328 	XDR_DESTROY(&(su->su_xdrs));
329 	mem_free(rpc_buffer(xprt), su->su_iosz);
330 	mem_free((caddr_t)su, sizeof(struct svcudp_data));
331 	mem_free((caddr_t)xprt, sizeof(SVCXPRT));
332 }
333 
334 
335 /***********this could be a separate file*********************/
336 
337 /*
338  * Fifo cache for udp server
339  * Copies pointers to reply buffers into fifo cache
340  * Buffers are sent again if retransmissions are detected.
341  */
342 
343 #define SPARSENESS 4	/* 75% sparse */
344 
345 #define CACHE_PERROR(msg)	\
346 	(void) fprintf(stderr,"%s\n", msg)
347 
348 #define ALLOC(type, size)	\
349 	(type *) mem_alloc((unsigned) (sizeof(type) * (size)))
350 
351 #define BZERO(addr, type, size)	 \
352 	memset(addr, 0, sizeof(type) * (int) (size))
353 
354 /*
355  * An entry in the cache
356  */
357 typedef struct cache_node *cache_ptr;
358 struct cache_node {
359 	/*
360 	 * Index into cache is xid, proc, vers, prog and address
361 	 */
362 	uint32_t cache_xid;
363 	rpcproc_t cache_proc;
364 	rpcvers_t cache_vers;
365 	rpcprog_t cache_prog;
366 	struct sockaddr_in cache_addr;
367 	/*
368 	 * The cached reply and length
369 	 */
370 	char * cache_reply;
371 	uint32_t cache_replylen;
372 	/*
373  	 * Next node on the list, if there is a collision
374 	 */
375 	cache_ptr cache_next;
376 };
377 
378 
379 
380 /*
381  * The entire cache
382  */
383 struct udp_cache {
384 	uint32_t uc_size;		/* size of cache */
385 	cache_ptr *uc_entries;	/* hash table of entries in cache */
386 	cache_ptr *uc_fifo;	/* fifo list of entries in cache */
387 	uint32_t uc_nextvictim;	/* points to next victim in fifo list */
388 	rpcprog_t uc_prog;		/* saved program number */
389 	rpcvers_t uc_vers;		/* saved version number */
390 	rpcproc_t uc_proc;		/* saved procedure number */
391 	struct sockaddr_in uc_addr; /* saved caller's address */
392 };
393 
394 
395 /*
396  * the hashing function
397  */
398 #define CACHE_LOC(transp, xid)	\
399  (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
400 
401 
402 /*
403  * Enable use of the cache.
404  * Note: there is no disable.
405  */
406 int
svcudp_enablecache(SVCXPRT * transp,uint32_t size)407 svcudp_enablecache(
408 	SVCXPRT *transp,
409 	uint32_t size)
410 {
411 	struct svcudp_data *su = su_data(transp);
412 	struct udp_cache *uc;
413 
414 	if (su->su_cache != NULL) {
415 		CACHE_PERROR("enablecache: cache already enabled");
416 		return(0);
417 	}
418 	uc = ALLOC(struct udp_cache, 1);
419 	if (uc == NULL) {
420 		CACHE_PERROR("enablecache: could not allocate cache");
421 		return(0);
422 	}
423 	uc->uc_size = size;
424 	uc->uc_nextvictim = 0;
425 	uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS);
426 	if (uc->uc_entries == NULL) {
427 		CACHE_PERROR("enablecache: could not allocate cache data");
428 		return(0);
429 	}
430 	BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS);
431 	uc->uc_fifo = ALLOC(cache_ptr, size);
432 	if (uc->uc_fifo == NULL) {
433 		CACHE_PERROR("enablecache: could not allocate cache fifo");
434 		return(0);
435 	}
436 	BZERO(uc->uc_fifo, cache_ptr, size);
437 	su->su_cache = (char *) uc;
438 	return(1);
439 }
440 
441 
442 /*
443  * Set an entry in the cache
444  */
445 static void
cache_set(SVCXPRT * xprt,uint32_t replylen)446 cache_set(
447 	SVCXPRT *xprt,
448 	uint32_t replylen)
449 {
450 	cache_ptr victim;
451 	cache_ptr *vicp;
452 	struct svcudp_data *su = su_data(xprt);
453 	struct udp_cache *uc = (struct udp_cache *) su->su_cache;
454 	u_int loc;
455 	char *newbuf;
456 
457 	/*
458  	 * Find space for the new entry, either by
459 	 * reusing an old entry, or by mallocing a new one
460 	 */
461 	victim = uc->uc_fifo[uc->uc_nextvictim];
462 	if (victim != NULL) {
463 		loc = CACHE_LOC(xprt, victim->cache_xid);
464 		for (vicp = &uc->uc_entries[loc];
465 		  *vicp != NULL && *vicp != victim;
466 		  vicp = &(*vicp)->cache_next)
467 				;
468 		if (*vicp == NULL) {
469 			CACHE_PERROR("cache_set: victim not found");
470 			return;
471 		}
472 		*vicp = victim->cache_next;	/* remote from cache */
473 		newbuf = victim->cache_reply;
474 	} else {
475 		victim = ALLOC(struct cache_node, 1);
476 		if (victim == NULL) {
477 			CACHE_PERROR("cache_set: victim alloc failed");
478 			return;
479 		}
480 		newbuf = mem_alloc(su->su_iosz);
481 		if (newbuf == NULL) {
482 			CACHE_PERROR("cache_set: could not allocate new rpc_buffer");
483 			free(victim);
484 			return;
485 		}
486 	}
487 
488 	/*
489 	 * Store it away
490 	 */
491 	victim->cache_replylen = replylen;
492 	victim->cache_reply = rpc_buffer(xprt);
493 	rpc_buffer(xprt) = newbuf;
494 	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE);
495 	victim->cache_xid = su->su_xid;
496 	victim->cache_proc = uc->uc_proc;
497 	victim->cache_vers = uc->uc_vers;
498 	victim->cache_prog = uc->uc_prog;
499 	victim->cache_addr = uc->uc_addr;
500 	loc = CACHE_LOC(xprt, victim->cache_xid);
501 	victim->cache_next = uc->uc_entries[loc];
502 	uc->uc_entries[loc] = victim;
503 	uc->uc_fifo[uc->uc_nextvictim++] = victim;
504 	uc->uc_nextvictim %= uc->uc_size;
505 }
506 
507 /*
508  * Try to get an entry from the cache
509  * return 1 if found, 0 if not found
510  */
511 static int
cache_get(SVCXPRT * xprt,struct rpc_msg * msg,char ** replyp,uint32_t * replylenp)512 cache_get(
513 	SVCXPRT *xprt,
514 	struct rpc_msg *msg,
515 	char **replyp,
516 	uint32_t *replylenp)
517 {
518 	u_int loc;
519 	cache_ptr ent;
520 	struct svcudp_data *su = su_data(xprt);
521 	struct udp_cache *uc = su->su_cache;
522 
523 #	define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
524 
525 	loc = CACHE_LOC(xprt, su->su_xid);
526 	for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
527 		if (ent->cache_xid == su->su_xid &&
528 		  ent->cache_proc == uc->uc_proc &&
529 		  ent->cache_vers == uc->uc_vers &&
530 		  ent->cache_prog == uc->uc_prog &&
531 		  EQADDR(ent->cache_addr, uc->uc_addr)) {
532 			*replyp = ent->cache_reply;
533 			*replylenp = ent->cache_replylen;
534 			return(1);
535 		}
536 	}
537 	/*
538 	 * Failed to find entry
539 	 * Remember a few things so we can do a set later
540 	 */
541 	uc->uc_proc = msg->rm_call.cb_proc;
542 	uc->uc_vers = msg->rm_call.cb_vers;
543 	uc->uc_prog = msg->rm_call.cb_prog;
544 	uc->uc_addr = xprt->xp_raddr;
545 	return(0);
546 }
547