Path: blob/main/crypto/krb5/src/lib/rpc/svc_udp.c
108061 views
/* @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC */1/*2* Copyright (c) 2010, Oracle America, Inc.3*4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions are met:8*9* * Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* * Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in14* the documentation and/or other materials provided with the15* distribution.16*17* * Neither the name of the "Oracle America, Inc." nor the names of18* its contributors may be used to endorse or promote products19* derived from this software without specific prior written permission.20*21* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS22* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED23* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A24* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT25* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,26* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED27* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR28* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF29* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING30* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS31* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.32*/33#if !defined(lint) && defined(SCCSIDS)34static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";35#endif3637/*38* svc_udp.c,39* Server side for UDP/IP based RPC. (Does some caching in the hopes of40* achieving execute-at-most-once semantics.)41*/4243#include "k5-platform.h"44#include <unistd.h>45#include <gssrpc/rpc.h>46#include <sys/socket.h>47#ifdef HAVE_SYS_UIO_H48#include <sys/uio.h>49#endif50#include <port-sockets.h>51#include <socket-utils.h>525354#ifndef GETSOCKNAME_ARG3_TYPE55#define GETSOCKNAME_ARG3_TYPE int56#endif5758#define rpc_buffer(xprt) ((xprt)->xp_p1)59#ifndef MAX60#define MAX(a, b) ((a > b) ? a : b)61#endif6263static bool_t svcudp_recv(SVCXPRT *, struct rpc_msg *);64static bool_t svcudp_reply(SVCXPRT *, struct rpc_msg *);65static enum xprt_stat svcudp_stat(SVCXPRT *);66static bool_t svcudp_getargs(SVCXPRT *, xdrproc_t, void *);67static bool_t svcudp_freeargs(SVCXPRT *, xdrproc_t, void *);68static void svcudp_destroy(SVCXPRT *);6970static void cache_set(SVCXPRT *, uint32_t);71static int cache_get(SVCXPRT *, struct rpc_msg *, char **, uint32_t *);7273static struct xp_ops svcudp_op = {74svcudp_recv,75svcudp_stat,76svcudp_getargs,77svcudp_reply,78svcudp_freeargs,79svcudp_destroy80};818283/*84* kept in xprt->xp_p285*/86struct svcudp_data {87u_int su_iosz; /* byte size of send.recv buffer */88uint32_t su_xid; /* transaction id */89XDR su_xdrs; /* XDR handle */90char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */91void * su_cache; /* cached data, NULL if no cache */92};93#define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))9495/*96* Usage:97* xprt = svcudp_create(sock);98*99* If sock<0 then a socket is created, else sock is used.100* If the socket, sock is not bound to a port then svcudp_create101* binds it to an arbitrary port. In any (successful) case,102* xprt->xp_sock is the registered socket number and xprt->xp_port is the103* associated port number.104* Once *xprt is initialized, it is registered as a transporter;105* see (svc.h, xprt_register).106* The routines returns NULL if a problem occurred.107*/108SVCXPRT *109svcudp_bufcreate(110int sock,111u_int sendsz,112u_int recvsz)113{114bool_t madesock = FALSE;115SVCXPRT *xprt;116struct svcudp_data *su;117struct sockaddr_storage ss;118struct sockaddr *sa = (struct sockaddr *)&ss;119socklen_t len;120121if (sock == RPC_ANYSOCK) {122if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {123perror("svcudp_create: socket creation problem");124return ((SVCXPRT *)NULL);125}126set_cloexec_fd(sock);127madesock = TRUE;128memset(&ss, 0, sizeof(ss));129sa->sa_family = AF_INET;130} else {131len = sizeof(struct sockaddr_storage);132if (getsockname(sock, sa, &len) < 0) {133perror("svcudp_create - cannot getsockname");134return ((SVCXPRT *)NULL);135}136}137138if (bindresvport_sa(sock, sa)) {139sa_setport(sa, 0);140(void)bind(sock, sa, sa_socklen(sa));141}142len = sizeof(struct sockaddr_storage);143if (getsockname(sock, sa, &len) != 0) {144perror("svcudp_create - cannot getsockname");145if (madesock)146(void)close(sock);147return ((SVCXPRT *)NULL);148}149xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));150if (xprt == NULL) {151(void)fprintf(stderr, "svcudp_create: out of memory\n");152return (NULL);153}154su = (struct svcudp_data *)mem_alloc(sizeof(*su));155if (su == NULL) {156(void)fprintf(stderr, "svcudp_create: out of memory\n");157return (NULL);158}159su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4;160if ((rpc_buffer(xprt) = mem_alloc(su->su_iosz)) == NULL) {161(void)fprintf(stderr, "svcudp_create: out of memory\n");162return (NULL);163}164xdrmem_create(165&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE);166su->su_cache = NULL;167xprt->xp_p2 = (caddr_t)su;168xprt->xp_auth = NULL;169xprt->xp_verf.oa_base = su->su_verfbody;170xprt->xp_ops = &svcudp_op;171xprt->xp_port = sa_getport(sa);172xprt->xp_sock = sock;173xprt_register(xprt);174return (xprt);175}176177SVCXPRT *178svcudp_create(int sock)179{180181return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE));182}183184static enum xprt_stat185svcudp_stat(SVCXPRT *xprt)186{187188return (XPRT_IDLE);189}190191static bool_t192svcudp_recv(193SVCXPRT *xprt,194struct rpc_msg *msg)195{196struct msghdr dummy;197struct iovec dummy_iov[1];198struct svcudp_data *su = su_data(xprt);199XDR *xdrs = &su->su_xdrs;200int rlen;201char *reply;202uint32_t replylen;203socklen_t addrlen;204205again:206memset(&dummy, 0, sizeof(dummy));207dummy_iov[0].iov_base = rpc_buffer(xprt);208dummy_iov[0].iov_len = (int) su->su_iosz;209dummy.msg_iov = dummy_iov;210dummy.msg_iovlen = 1;211dummy.msg_namelen = xprt->xp_laddrlen = sizeof(struct sockaddr_in);212dummy.msg_name = (char *) &xprt->xp_laddr;213rlen = recvmsg(xprt->xp_sock, &dummy, MSG_PEEK);214if (rlen == -1) {215if (errno == EINTR)216goto again;217else218return (FALSE);219}220221addrlen = sizeof(struct sockaddr_in);222rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz,2230, (struct sockaddr *)&(xprt->xp_raddr), &addrlen);224if (rlen == -1 && errno == EINTR)225goto again;226if (rlen < (int) (4*sizeof(uint32_t)))227return (FALSE);228xprt->xp_addrlen = addrlen;229xdrs->x_op = XDR_DECODE;230XDR_SETPOS(xdrs, 0);231if (! xdr_callmsg(xdrs, msg))232return (FALSE);233su->su_xid = msg->rm_xid;234if (su->su_cache != NULL) {235if (cache_get(xprt, msg, &reply, &replylen)) {236(void) sendto(xprt->xp_sock, reply, (int) replylen, 0,237(struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen);238return (TRUE);239}240}241return (TRUE);242}243244static bool_t svcudp_reply(245SVCXPRT *xprt,246struct rpc_msg *msg)247{248struct svcudp_data *su = su_data(xprt);249XDR *xdrs = &su->su_xdrs;250u_int slen;251bool_t stat = FALSE;252ssize_t r;253254xdrproc_t xdr_results = NULL;255caddr_t xdr_location = 0;256bool_t has_args;257258if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&259msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {260has_args = TRUE;261xdr_results = msg->acpted_rply.ar_results.proc;262xdr_location = msg->acpted_rply.ar_results.where;263264msg->acpted_rply.ar_results.proc = xdr_void;265msg->acpted_rply.ar_results.where = NULL;266} else267has_args = FALSE;268269xdrs->x_op = XDR_ENCODE;270XDR_SETPOS(xdrs, 0);271msg->rm_xid = su->su_xid;272if (xdr_replymsg(xdrs, msg) &&273(!has_args ||274(SVCAUTH_WRAP(xprt->xp_auth, xdrs, xdr_results, xdr_location)))) {275slen = XDR_GETPOS(xdrs);276r = sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0,277(struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen);278if (r >= 0 && (u_int)r == slen) {279stat = TRUE;280if (su->su_cache) {281cache_set(xprt, (uint32_t) slen);282}283}284}285return (stat);286}287288static bool_t289svcudp_getargs(290SVCXPRT *xprt,291xdrproc_t xdr_args,292void * args_ptr)293{294if (! SVCAUTH_UNWRAP(xprt->xp_auth, &(su_data(xprt)->su_xdrs),295xdr_args, args_ptr)) {296(void)svcudp_freeargs(xprt, xdr_args, args_ptr);297return FALSE;298}299return TRUE;300}301302static bool_t303svcudp_freeargs(304SVCXPRT *xprt,305xdrproc_t xdr_args,306void * args_ptr)307{308XDR *xdrs = &su_data(xprt)->su_xdrs;309310xdrs->x_op = XDR_FREE;311return ((*xdr_args)(xdrs, args_ptr));312}313314static void315svcudp_destroy(SVCXPRT *xprt)316{317struct svcudp_data *su = su_data(xprt);318319xprt_unregister(xprt);320if (xprt->xp_sock != INVALID_SOCKET)321(void)closesocket(xprt->xp_sock);322xprt->xp_sock = INVALID_SOCKET;323if (xprt->xp_auth != NULL) {324SVCAUTH_DESTROY(xprt->xp_auth);325xprt->xp_auth = NULL;326}327XDR_DESTROY(&(su->su_xdrs));328mem_free(rpc_buffer(xprt), su->su_iosz);329mem_free((caddr_t)su, sizeof(struct svcudp_data));330mem_free((caddr_t)xprt, sizeof(SVCXPRT));331}332333334/***********this could be a separate file*********************/335336/*337* Fifo cache for udp server338* Copies pointers to reply buffers into fifo cache339* Buffers are sent again if retransmissions are detected.340*/341342#define SPARSENESS 4 /* 75% sparse */343344#define CACHE_PERROR(msg) \345(void) fprintf(stderr,"%s\n", msg)346347#define ALLOC(type, size) \348(type *) mem_alloc((unsigned) (sizeof(type) * (size)))349350#define BZERO(addr, type, size) \351memset(addr, 0, sizeof(type) * (int) (size))352353/*354* An entry in the cache355*/356typedef struct cache_node *cache_ptr;357struct cache_node {358/*359* Index into cache is xid, proc, vers, prog and address360*/361uint32_t cache_xid;362rpcproc_t cache_proc;363rpcvers_t cache_vers;364rpcprog_t cache_prog;365struct sockaddr_in cache_addr;366/*367* The cached reply and length368*/369char * cache_reply;370uint32_t cache_replylen;371/*372* Next node on the list, if there is a collision373*/374cache_ptr cache_next;375};376377378379/*380* The entire cache381*/382struct udp_cache {383uint32_t uc_size; /* size of cache */384cache_ptr *uc_entries; /* hash table of entries in cache */385cache_ptr *uc_fifo; /* fifo list of entries in cache */386uint32_t uc_nextvictim; /* points to next victim in fifo list */387rpcprog_t uc_prog; /* saved program number */388rpcvers_t uc_vers; /* saved version number */389rpcproc_t uc_proc; /* saved procedure number */390struct sockaddr_in uc_addr; /* saved caller's address */391};392393394/*395* the hashing function396*/397#define CACHE_LOC(transp, xid) \398(xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))399400401/*402* Enable use of the cache.403* Note: there is no disable.404*/405int406svcudp_enablecache(407SVCXPRT *transp,408uint32_t size)409{410struct svcudp_data *su = su_data(transp);411struct udp_cache *uc;412413if (su->su_cache != NULL) {414CACHE_PERROR("enablecache: cache already enabled");415return(0);416}417uc = ALLOC(struct udp_cache, 1);418if (uc == NULL) {419CACHE_PERROR("enablecache: could not allocate cache");420return(0);421}422uc->uc_size = size;423uc->uc_nextvictim = 0;424uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS);425if (uc->uc_entries == NULL) {426CACHE_PERROR("enablecache: could not allocate cache data");427return(0);428}429BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS);430uc->uc_fifo = ALLOC(cache_ptr, size);431if (uc->uc_fifo == NULL) {432CACHE_PERROR("enablecache: could not allocate cache fifo");433return(0);434}435BZERO(uc->uc_fifo, cache_ptr, size);436su->su_cache = (char *) uc;437return(1);438}439440441/*442* Set an entry in the cache443*/444static void445cache_set(446SVCXPRT *xprt,447uint32_t replylen)448{449cache_ptr victim;450cache_ptr *vicp;451struct svcudp_data *su = su_data(xprt);452struct udp_cache *uc = (struct udp_cache *) su->su_cache;453u_int loc;454char *newbuf;455456/*457* Find space for the new entry, either by458* reusing an old entry, or by mallocing a new one459*/460victim = uc->uc_fifo[uc->uc_nextvictim];461if (victim != NULL) {462loc = CACHE_LOC(xprt, victim->cache_xid);463for (vicp = &uc->uc_entries[loc];464*vicp != NULL && *vicp != victim;465vicp = &(*vicp)->cache_next)466;467if (*vicp == NULL) {468CACHE_PERROR("cache_set: victim not found");469return;470}471*vicp = victim->cache_next; /* remote from cache */472newbuf = victim->cache_reply;473} else {474victim = ALLOC(struct cache_node, 1);475if (victim == NULL) {476CACHE_PERROR("cache_set: victim alloc failed");477return;478}479newbuf = mem_alloc(su->su_iosz);480if (newbuf == NULL) {481CACHE_PERROR("cache_set: could not allocate new rpc_buffer");482free(victim);483return;484}485}486487/*488* Store it away489*/490victim->cache_replylen = replylen;491victim->cache_reply = rpc_buffer(xprt);492rpc_buffer(xprt) = newbuf;493xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE);494victim->cache_xid = su->su_xid;495victim->cache_proc = uc->uc_proc;496victim->cache_vers = uc->uc_vers;497victim->cache_prog = uc->uc_prog;498victim->cache_addr = uc->uc_addr;499loc = CACHE_LOC(xprt, victim->cache_xid);500victim->cache_next = uc->uc_entries[loc];501uc->uc_entries[loc] = victim;502uc->uc_fifo[uc->uc_nextvictim++] = victim;503uc->uc_nextvictim %= uc->uc_size;504}505506/*507* Try to get an entry from the cache508* return 1 if found, 0 if not found509*/510static int511cache_get(512SVCXPRT *xprt,513struct rpc_msg *msg,514char **replyp,515uint32_t *replylenp)516{517u_int loc;518cache_ptr ent;519struct svcudp_data *su = su_data(xprt);520struct udp_cache *uc = su->su_cache;521522# define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)523524loc = CACHE_LOC(xprt, su->su_xid);525for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {526if (ent->cache_xid == su->su_xid &&527ent->cache_proc == uc->uc_proc &&528ent->cache_vers == uc->uc_vers &&529ent->cache_prog == uc->uc_prog &&530EQADDR(ent->cache_addr, uc->uc_addr)) {531*replyp = ent->cache_reply;532*replylenp = ent->cache_replylen;533return(1);534}535}536/*537* Failed to find entry538* Remember a few things so we can do a set later539*/540uc->uc_proc = msg->rm_call.cb_proc;541uc->uc_vers = msg->rm_call.cb_vers;542uc->uc_prog = msg->rm_call.cb_prog;543uc->uc_addr = xprt->xp_raddr;544return(0);545}546547548