/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2014 Gary Mills * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley * 4.3 BSD under license from the Regents of the University of * California. */ /* * svc_dg.c, Server side for connectionless RPC. * * Does some caching in the hopes of achieving execute-at-most-once semantics. */ #include "mt.h" #include "rpc_mt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef RPC_CACHE_DEBUG #include #include #endif #ifndef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif static struct xp_ops *svc_dg_ops(); static void cache_set(); static int cache_get(); #define rpc_buffer(xprt) ((xprt)->xp_p1) /* * Usage: * xprt = svc_dg_create(sock, sendsize, recvsize); * Does other connectionless specific initializations. * Once *xprt is initialized, it is registered. * see (svc.h, xprt_register). If recvsize or sendsize are 0 suitable * system defaults are chosen. * The routines returns NULL if a problem occurred. */ static const char svc_dg_str[] = "svc_dg_create: %s"; static const char svc_dg_err1[] = "could not get transport information"; static const char svc_dg_err2[] = " transport does not support data transfer"; static const char svc_dg_err3[] = "fd > FD_SETSIZE; Use rpc_control(RPC_SVC_USE_POLLFD,...);"; static const char __no_mem_str[] = "out of memory"; /* Structure used to initialize SVC_XP_AUTH(xprt).svc_ah_ops. */ extern struct svc_auth_ops svc_auth_any_ops; extern int __rpc_get_ltaddr(struct netbuf *, struct netbuf *); void svc_dg_xprtfree(SVCXPRT *xprt) { /* LINTED pointer alignment */ SVCXPRT_EXT *xt = xprt ? SVCEXT(xprt) : NULL; /* LINTED pointer alignment */ struct svc_dg_data *su = xprt ? get_svc_dg_data(xprt) : NULL; if (xprt == NULL) return; if (xprt->xp_netid) free(xprt->xp_netid); if (xprt->xp_tp) free(xprt->xp_tp); if (xt->parent == NULL) if (xprt->xp_ltaddr.buf) free(xprt->xp_ltaddr.buf); if (xprt->xp_rtaddr.buf) free(xprt->xp_rtaddr.buf); if (su != NULL) { XDR_DESTROY(&(su->su_xdrs)); free(su); } if (rpc_buffer(xprt)) free(rpc_buffer(xprt)); svc_xprt_free(xprt); } SVCXPRT * svc_dg_create_private(int fd, uint_t sendsize, uint_t recvsize) { SVCXPRT *xprt; struct svc_dg_data *su = NULL; struct t_info tinfo; size_t ucred_sz = ucred_size(); if (RPC_FD_NOTIN_FDSET(fd)) { errno = EBADF; t_errno = TBADF; syslog(LOG_ERR, svc_dg_str, svc_dg_err3); return (NULL); } if (t_getinfo(fd, &tinfo) == -1) { syslog(LOG_ERR, svc_dg_str, svc_dg_err1); return (NULL); } /* * Find the receive and the send size */ sendsize = __rpc_get_t_size((int)sendsize, tinfo.tsdu); recvsize = __rpc_get_t_size((int)recvsize, tinfo.tsdu); if ((sendsize == 0) || (recvsize == 0)) { syslog(LOG_ERR, svc_dg_str, svc_dg_err2); return (NULL); } if ((xprt = svc_xprt_alloc()) == NULL) goto freedata; /* LINTED pointer alignment */ svc_flags(xprt) |= SVC_DGRAM; su = malloc(sizeof (*su) + ucred_sz); if (su == NULL) goto freedata; su->su_iosz = ((MAX(sendsize, recvsize) + 3) / 4) * 4; if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL) goto freedata; xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE); su->su_cache = NULL; xprt->xp_fd = fd; xprt->xp_p2 = (caddr_t)su; xprt->xp_verf.oa_base = su->su_verfbody; xprt->xp_ops = svc_dg_ops(); su->su_tudata.addr.maxlen = 0; /* Fill in later */ su->su_tudata.udata.buf = (char *)rpc_buffer(xprt); su->su_tudata.opt.buf = (char *)su->opts; su->su_tudata.udata.maxlen = su->su_iosz; su->su_tudata.opt.maxlen = MAX_OPT_WORDS * sizeof (int) + ucred_sz; /* LINTED pointer alignment */ SVC_XP_AUTH(xprt).svc_ah_ops = svc_auth_any_ops; /* LINTED pointer alignment */ SVC_XP_AUTH(xprt).svc_ah_private = NULL; return (xprt); freedata: (void) syslog(LOG_ERR, svc_dg_str, __no_mem_str); if (xprt) svc_dg_xprtfree(xprt); return (NULL); } SVCXPRT * svc_dg_create(const int fd, const uint_t sendsize, const uint_t recvsize) { SVCXPRT *xprt; if ((xprt = svc_dg_create_private(fd, sendsize, recvsize)) != NULL) xprt_register(xprt); return (xprt); } SVCXPRT * svc_dg_xprtcopy(SVCXPRT *parent) { SVCXPRT *xprt; struct svc_dg_data *su; size_t ucred_sz = ucred_size(); if ((xprt = svc_xprt_alloc()) == NULL) return (NULL); /* LINTED pointer alignment */ SVCEXT(xprt)->parent = parent; /* LINTED pointer alignment */ SVCEXT(xprt)->flags = SVCEXT(parent)->flags; xprt->xp_fd = parent->xp_fd; xprt->xp_port = parent->xp_port; xprt->xp_ops = svc_dg_ops(); if (parent->xp_tp) { xprt->xp_tp = (char *)strdup(parent->xp_tp); if (xprt->xp_tp == NULL) { syslog(LOG_ERR, "svc_dg_xprtcopy: strdup failed"); svc_dg_xprtfree(xprt); return (NULL); } } if (parent->xp_netid) { xprt->xp_netid = (char *)strdup(parent->xp_netid); if (xprt->xp_netid == NULL) { syslog(LOG_ERR, "svc_dg_xprtcopy: strdup failed"); if (parent->xp_tp) free(parent->xp_tp); svc_dg_xprtfree(xprt); return (NULL); } } xprt->xp_ltaddr = parent->xp_ltaddr; /* shared with parent */ xprt->xp_rtaddr = parent->xp_rtaddr; xprt->xp_rtaddr.buf = malloc(xprt->xp_rtaddr.maxlen); if (xprt->xp_rtaddr.buf == NULL) { svc_dg_xprtfree(xprt); return (NULL); } (void) memcpy(xprt->xp_rtaddr.buf, parent->xp_rtaddr.buf, xprt->xp_rtaddr.maxlen); xprt->xp_type = parent->xp_type; if ((su = malloc(sizeof (struct svc_dg_data) + ucred_sz)) == NULL) { svc_dg_xprtfree(xprt); return (NULL); } /* LINTED pointer alignment */ su->su_iosz = get_svc_dg_data(parent)->su_iosz; if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL) { svc_dg_xprtfree(xprt); free(su); return (NULL); } xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE); su->su_cache = NULL; su->su_tudata.addr.maxlen = 0; /* Fill in later */ su->su_tudata.udata.buf = (char *)rpc_buffer(xprt); su->su_tudata.opt.buf = (char *)su->opts; su->su_tudata.udata.maxlen = su->su_iosz; su->su_tudata.opt.maxlen = MAX_OPT_WORDS * sizeof (int) + ucred_sz; xprt->xp_p2 = (caddr_t)su; /* get_svc_dg_data(xprt) = su */ xprt->xp_verf.oa_base = su->su_verfbody; return (xprt); } /*ARGSUSED*/ static enum xprt_stat svc_dg_stat(SVCXPRT *xprt) { return (XPRT_IDLE); } /* * Find the SCM_UCRED in src and place a pointer to that option alone in dest. * Note that these two 'netbuf' structures might be the same one, so the code * has to be careful about referring to src after changing dest. */ static void extract_cred(const struct netbuf *src, struct netbuf *dest) { char *cp = src->buf; unsigned int len = src->len; const struct T_opthdr *opt; unsigned int olen; while (len >= sizeof (*opt)) { /* LINTED: pointer alignment */ opt = (const struct T_opthdr *)cp; olen = opt->len; if (olen > len || olen < sizeof (*opt) || !IS_P2ALIGNED(olen, sizeof (t_uscalar_t))) break; if (opt->level == SOL_SOCKET && opt->name == SCM_UCRED) { dest->buf = cp; dest->len = olen; return; } cp += olen; len -= olen; } dest->len = 0; } /* * This routine extracts the destination IP address of the inbound RPC packet * and sets that as source IP address for the outbound response. */ static void set_src_addr(SVCXPRT *xprt, struct netbuf *opt) { struct netbuf *nbufp, *ltaddr; struct T_opthdr *opthdr; in_pktinfo_t *pktinfo; struct sockaddr_in *sock = (struct sockaddr_in *)NULL; /* extract dest IP of inbound packet */ /* LINTED pointer alignment */ nbufp = (struct netbuf *)xprt->xp_p2; ltaddr = &xprt->xp_ltaddr; if (__rpc_get_ltaddr(nbufp, ltaddr) != 0) return; /* do nothing for non-IPv4 packet */ /* LINTED pointer alignment */ sock = (struct sockaddr_in *)ltaddr->buf; if (sock->sin_family != AF_INET) return; /* set desired option header */ opthdr = (struct T_opthdr *)memalign(sizeof (int), sizeof (struct T_opthdr) + sizeof (in_pktinfo_t)); if (opthdr == NULL) return; opthdr->len = sizeof (struct T_opthdr) + sizeof (in_pktinfo_t); opthdr->level = IPPROTO_IP; opthdr->name = IP_PKTINFO; /* * 1. set source IP of outbound packet * 2. value '0' for index means IP layer uses this as source address */ pktinfo = (in_pktinfo_t *)(opthdr + 1); (void) memset(pktinfo, 0, sizeof (in_pktinfo_t)); pktinfo->ipi_spec_dst.s_addr = sock->sin_addr.s_addr; pktinfo->ipi_ifindex = 0; /* copy data into ancillary buffer */ if (opthdr->len + opt->len <= opt->maxlen) { (void) memcpy((void *)(opt->buf+opt->len), (const void *)opthdr, opthdr->len); opt->len += opthdr->len; } free(opthdr); } static bool_t svc_dg_recv(SVCXPRT *xprt, struct rpc_msg *msg) { /* LINTED pointer alignment */ struct svc_dg_data *su = get_svc_dg_data(xprt); XDR *xdrs = &(su->su_xdrs); struct t_unitdata *tu_data = &(su->su_tudata); int moreflag; struct netbuf *nbufp; struct netconfig *nconf; /* XXX: tudata should have been made a part of the server handle */ if (tu_data->addr.maxlen == 0) tu_data->addr = xprt->xp_rtaddr; again: tu_data->addr.len = 0; tu_data->opt.len = 0; tu_data->udata.len = 0; moreflag = 0; if (t_rcvudata(xprt->xp_fd, tu_data, &moreflag) == -1) { #ifdef RPC_DEBUG syslog(LOG_ERR, "svc_dg_recv: t_rcvudata t_errno=%d errno=%d\n", t_errno, errno); #endif if (t_errno == TLOOK) { int lookres; lookres = t_look(xprt->xp_fd); if ((lookres == T_UDERR) && (t_rcvuderr(xprt->xp_fd, (struct t_uderr *)0) < 0)) { /*EMPTY*/ #ifdef RPC_DEBUG syslog(LOG_ERR, "svc_dg_recv: t_rcvuderr t_errno = %d\n", t_errno); #endif } if (lookres == T_DATA) goto again; } else if ((errno == EINTR) && (t_errno == TSYSERR)) goto again; else { return (FALSE); } } if ((moreflag) || (tu_data->udata.len < 4 * (uint_t)sizeof (uint32_t))) { /* * If moreflag is set, drop that data packet. Something wrong */ return (FALSE); } su->optbuf = tu_data->opt; xprt->xp_rtaddr.len = tu_data->addr.len; xdrs->x_op = XDR_DECODE; XDR_SETPOS(xdrs, 0); if (!xdr_callmsg(xdrs, msg)) return (FALSE); su->su_xid = msg->rm_xid; if (su->su_cache != NULL) { char *reply; uint32_t replylen; if (cache_get(xprt, msg, &reply, &replylen)) { /* tu_data.addr is already set */ tu_data->udata.buf = reply; tu_data->udata.len = (uint_t)replylen; extract_cred(&tu_data->opt, &tu_data->opt); set_src_addr(xprt, &tu_data->opt); (void) t_sndudata(xprt->xp_fd, tu_data); tu_data->udata.buf = (char *)rpc_buffer(xprt); tu_data->opt.buf = (char *)su->opts; return (FALSE); } } /* * get local ip address */ if ((nconf = getnetconfigent(xprt->xp_netid)) != NULL) { if (strcmp(nconf->nc_protofmly, NC_INET) == 0 || strcmp(nconf->nc_protofmly, NC_INET6) == 0) { if (nconf->nc_semantics == NC_TPI_CLTS) { /* LINTED pointer cast */ nbufp = (struct netbuf *)(xprt->xp_p2); if (__rpc_get_ltaddr(nbufp, &xprt->xp_ltaddr) < 0) { if (strcmp(nconf->nc_protofmly, NC_INET) == 0) { syslog(LOG_ERR, "svc_dg_recv: ip(udp), " "t_errno=%d, errno=%d", t_errno, errno); } if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) { syslog(LOG_ERR, "svc_dg_recv: ip (udp6), " "t_errno=%d, errno=%d", t_errno, errno); } freenetconfigent(nconf); return (FALSE); } } } freenetconfigent(nconf); } return (TRUE); } static bool_t svc_dg_reply(SVCXPRT *xprt, struct rpc_msg *msg) { /* LINTED pointer alignment */ struct svc_dg_data *su = get_svc_dg_data(xprt); XDR *xdrs = &(su->su_xdrs); bool_t stat = FALSE; xdrproc_t xdr_results; caddr_t xdr_location; bool_t has_args; if (msg->rm_reply.rp_stat == MSG_ACCEPTED && msg->rm_reply.rp_acpt.ar_stat == SUCCESS) { has_args = TRUE; xdr_results = msg->acpted_rply.ar_results.proc; xdr_location = msg->acpted_rply.ar_results.where; msg->acpted_rply.ar_results.proc = xdr_void; msg->acpted_rply.ar_results.where = NULL; } else has_args = FALSE; xdrs->x_op = XDR_ENCODE; XDR_SETPOS(xdrs, 0); msg->rm_xid = su->su_xid; if (xdr_replymsg(xdrs, msg) && (!has_args || /* LINTED pointer alignment */ SVCAUTH_WRAP(&SVC_XP_AUTH(xprt), xdrs, xdr_results, xdr_location))) { int slen; struct t_unitdata *tu_data = &(su->su_tudata); slen = (int)XDR_GETPOS(xdrs); tu_data->udata.len = slen; extract_cred(&su->optbuf, &tu_data->opt); set_src_addr(xprt, &tu_data->opt); try_again: if (t_sndudata(xprt->xp_fd, tu_data) == 0) { stat = TRUE; if (su->su_cache && slen >= 0) { cache_set(xprt, (uint32_t)slen); } } else { if (errno == EINTR) goto try_again; syslog(LOG_ERR, "svc_dg_reply: t_sndudata error t_errno=%d ", "errno=%d\n", t_errno, errno); } tu_data->opt.buf = (char *)su->opts; } return (stat); } static bool_t svc_dg_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) { if (svc_mt_mode != RPC_SVC_MT_NONE) svc_args_done(xprt); /* LINTED pointer alignment */ return (SVCAUTH_UNWRAP(&SVC_XP_AUTH(xprt), &(get_svc_dg_data(xprt)->su_xdrs), xdr_args, args_ptr)); } static bool_t svc_dg_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) { /* LINTED pointer alignment */ XDR *xdrs = &(get_svc_dg_data(xprt)->su_xdrs); xdrs->x_op = XDR_FREE; return ((*xdr_args)(xdrs, args_ptr)); } static void svc_dg_destroy(SVCXPRT *xprt) { (void) mutex_lock(&svc_mutex); _svc_dg_destroy_private(xprt); (void) mutex_unlock(&svc_mutex); } void _svc_dg_destroy_private(SVCXPRT *xprt) { if (svc_mt_mode != RPC_SVC_MT_NONE) { /* LINTED pointer alignment */ if (SVCEXT(xprt)->parent) /* LINTED pointer alignment */ xprt = SVCEXT(xprt)->parent; /* LINTED pointer alignment */ svc_flags(xprt) |= SVC_DEFUNCT; /* LINTED pointer alignment */ if (SVCEXT(xprt)->refcnt > 0) return; } xprt_unregister(xprt); (void) t_close(xprt->xp_fd); if (svc_mt_mode != RPC_SVC_MT_NONE) svc_xprt_destroy(xprt); else svc_dg_xprtfree(xprt); } /*ARGSUSED*/ static bool_t svc_dg_control(SVCXPRT *xprt, const uint_t rq, void *in) { switch (rq) { case SVCGET_XID: if (xprt->xp_p2 == NULL) return (FALSE); /* LINTED pointer alignment */ *(uint32_t *)in = ((struct svc_dg_data *)(xprt->xp_p2))->su_xid; return (TRUE); default: return (FALSE); } } static struct xp_ops * svc_dg_ops(void) { static struct xp_ops ops; extern mutex_t ops_lock; /* VARIABLES PROTECTED BY ops_lock: ops */ (void) mutex_lock(&ops_lock); if (ops.xp_recv == NULL) { ops.xp_recv = svc_dg_recv; ops.xp_stat = svc_dg_stat; ops.xp_getargs = svc_dg_getargs; ops.xp_reply = svc_dg_reply; ops.xp_freeargs = svc_dg_freeargs; ops.xp_destroy = svc_dg_destroy; ops.xp_control = svc_dg_control; } (void) mutex_unlock(&ops_lock); return (&ops); } /* The CACHING COMPONENT */ /* * Could have been a separate file, but some part of it depends upon the * private structure of the client handle. * * Fifo cache for cl server * Copies pointers to reply buffers into fifo cache * Buffers are sent again if retransmissions are detected. */ #define SPARSENESS 4 /* 75% sparse */ /* * An entry in the cache */ typedef struct cache_node *cache_ptr; struct cache_node { /* * Index into cache is xid, proc, vers, prog and address */ uint32_t cache_xid; rpcproc_t cache_proc; rpcvers_t cache_vers; rpcprog_t cache_prog; struct netbuf cache_addr; /* * The cached reply and length */ char *cache_reply; uint32_t cache_replylen; /* * Next node on the list, if there is a collision */ cache_ptr cache_next; }; /* * The entire cache */ struct cl_cache { uint32_t uc_size; /* size of cache */ cache_ptr *uc_entries; /* hash table of entries in cache */ cache_ptr *uc_fifo; /* fifo list of entries in cache */ uint32_t uc_nextvictim; /* points to next victim in fifo list */ rpcprog_t uc_prog; /* saved program number */ rpcvers_t uc_vers; /* saved version number */ rpcproc_t uc_proc; /* saved procedure number */ }; /* * the hashing function */ #define CACHE_LOC(transp, xid) \ (xid % (SPARSENESS * ((struct cl_cache *) \ get_svc_dg_data(transp)->su_cache)->uc_size)) extern mutex_t dupreq_lock; /* * Enable use of the cache. Returns 1 on success, 0 on failure. * Note: there is no disable. */ static const char cache_enable_str[] = "svc_enablecache: %s %s"; static const char alloc_err[] = "could not allocate cache "; static const char enable_err[] = "cache already enabled"; int svc_dg_enablecache(SVCXPRT *xprt, const uint_t size) { SVCXPRT *transp; struct svc_dg_data *su; struct cl_cache *uc; /* LINTED pointer alignment */ if (svc_mt_mode != RPC_SVC_MT_NONE && SVCEXT(xprt)->parent != NULL) /* LINTED pointer alignment */ transp = SVCEXT(xprt)->parent; else transp = xprt; /* LINTED pointer alignment */ su = get_svc_dg_data(transp); (void) mutex_lock(&dupreq_lock); if (su->su_cache != NULL) { (void) syslog(LOG_ERR, cache_enable_str, enable_err, " "); (void) mutex_unlock(&dupreq_lock); return (0); } uc = malloc(sizeof (struct cl_cache)); if (uc == NULL) { (void) syslog(LOG_ERR, cache_enable_str, alloc_err, " "); (void) mutex_unlock(&dupreq_lock); return (0); } uc->uc_size = size; uc->uc_nextvictim = 0; uc->uc_entries = calloc(size * SPARSENESS, sizeof (cache_ptr)); if (uc->uc_entries == NULL) { (void) syslog(LOG_ERR, cache_enable_str, alloc_err, "data"); free(uc); (void) mutex_unlock(&dupreq_lock); return (0); } uc->uc_fifo = calloc(size, sizeof (cache_ptr)); if (uc->uc_fifo == NULL) { (void) syslog(LOG_ERR, cache_enable_str, alloc_err, "fifo"); free(uc->uc_entries); free(uc); (void) mutex_unlock(&dupreq_lock); return (0); } su->su_cache = (char *)uc; (void) mutex_unlock(&dupreq_lock); return (1); } /* * Set an entry in the cache. It assumes that the uc entry is set from * the earlier call to cache_get() for the same procedure. This will always * happen because cache_get() is calle by svc_dg_recv and cache_set() is called * by svc_dg_reply(). All this hoopla because the right RPC parameters are * not available at svc_dg_reply time. */ static const char cache_set_str[] = "cache_set: %s"; static const char cache_set_err1[] = "victim not found"; static const char cache_set_err2[] = "victim alloc failed"; static const char cache_set_err3[] = "could not allocate new rpc buffer"; static void cache_set(SVCXPRT *xprt, uint32_t replylen) { SVCXPRT *parent; cache_ptr victim; cache_ptr *vicp; struct svc_dg_data *su; struct cl_cache *uc; uint_t loc; char *newbuf, *newbuf2; int my_mallocs = 0; #ifdef RPC_CACHE_DEBUG struct netconfig *nconf; char *uaddr; #endif /* LINTED pointer alignment */ if (svc_mt_mode != RPC_SVC_MT_NONE && SVCEXT(xprt)->parent != NULL) /* LINTED pointer alignment */ parent = SVCEXT(xprt)->parent; else parent = xprt; /* LINTED pointer alignment */ su = get_svc_dg_data(xprt); /* LINTED pointer alignment */ uc = (struct cl_cache *)get_svc_dg_data(parent)->su_cache; (void) mutex_lock(&dupreq_lock); /* * Find space for the new entry, either by * reusing an old entry, or by mallocing a new one */ victim = uc->uc_fifo[uc->uc_nextvictim]; if (victim != NULL) { /* LINTED pointer alignment */ loc = CACHE_LOC(parent, victim->cache_xid); for (vicp = &uc->uc_entries[loc]; *vicp != NULL && *vicp != victim; vicp = &(*vicp)->cache_next) ; if (*vicp == NULL) { (void) syslog(LOG_ERR, cache_set_str, cache_set_err1); (void) mutex_unlock(&dupreq_lock); return; } *vicp = victim->cache_next; /* remove from cache */ newbuf = victim->cache_reply; } else { victim = malloc(sizeof (struct cache_node)); if (victim == NULL) { (void) syslog(LOG_ERR, cache_set_str, cache_set_err2); (void) mutex_unlock(&dupreq_lock); return; } newbuf = malloc(su->su_iosz); if (newbuf == NULL) { (void) syslog(LOG_ERR, cache_set_str, cache_set_err3); free(victim); (void) mutex_unlock(&dupreq_lock); return; } my_mallocs = 1; } /* * Store it away */ #ifdef RPC_CACHE_DEBUG if (nconf = getnetconfigent(xprt->xp_netid)) { uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr); freenetconfigent(nconf); printf( "cache set for xid= %x prog=%d vers=%d proc=%d for rmtaddr=%s\n", su->su_xid, uc->uc_prog, uc->uc_vers, uc->uc_proc, uaddr); free(uaddr); } #endif newbuf2 = malloc(sizeof (char) * xprt->xp_rtaddr.len); if (newbuf2 == NULL) { syslog(LOG_ERR, "cache_set : out of memory"); if (my_mallocs) { free(victim); free(newbuf); } (void) mutex_unlock(&dupreq_lock); return; } victim->cache_replylen = replylen; victim->cache_reply = rpc_buffer(xprt); rpc_buffer(xprt) = newbuf; xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE); su->su_tudata.udata.buf = (char *)rpc_buffer(xprt); victim->cache_xid = su->su_xid; victim->cache_proc = uc->uc_proc; victim->cache_vers = uc->uc_vers; victim->cache_prog = uc->uc_prog; victim->cache_addr = xprt->xp_rtaddr; victim->cache_addr.buf = newbuf2; (void) memcpy(victim->cache_addr.buf, xprt->xp_rtaddr.buf, (int)xprt->xp_rtaddr.len); /* LINTED pointer alignment */ loc = CACHE_LOC(parent, victim->cache_xid); victim->cache_next = uc->uc_entries[loc]; uc->uc_entries[loc] = victim; uc->uc_fifo[uc->uc_nextvictim++] = victim; uc->uc_nextvictim %= uc->uc_size; (void) mutex_unlock(&dupreq_lock); } /* * Try to get an entry from the cache * return 1 if found, 0 if not found and set the stage for cache_set() */ static int cache_get(SVCXPRT *xprt, struct rpc_msg *msg, char **replyp, uint32_t *replylenp) { SVCXPRT *parent; uint_t loc; cache_ptr ent; struct svc_dg_data *su; struct cl_cache *uc; #ifdef RPC_CACHE_DEBUG struct netconfig *nconf; char *uaddr; #endif /* LINTED pointer alignment */ if (svc_mt_mode != RPC_SVC_MT_NONE && SVCEXT(xprt)->parent != NULL) /* LINTED pointer alignment */ parent = SVCEXT(xprt)->parent; else parent = xprt; /* LINTED pointer alignment */ su = get_svc_dg_data(xprt); /* LINTED pointer alignment */ uc = (struct cl_cache *)get_svc_dg_data(parent)->su_cache; (void) mutex_lock(&dupreq_lock); /* LINTED pointer alignment */ loc = CACHE_LOC(parent, su->su_xid); for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) { if (ent->cache_xid == su->su_xid && ent->cache_proc == msg->rm_call.cb_proc && ent->cache_vers == msg->rm_call.cb_vers && ent->cache_prog == msg->rm_call.cb_prog && ent->cache_addr.len == xprt->xp_rtaddr.len && (memcmp(ent->cache_addr.buf, xprt->xp_rtaddr.buf, xprt->xp_rtaddr.len) == 0)) { #ifdef RPC_CACHE_DEBUG if (nconf = getnetconfigent(xprt->xp_netid)) { uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr); freenetconfigent(nconf); printf( "cache entry found for xid=%x prog=%d vers=%d proc=%d for rmtaddr=%s\n", su->su_xid, msg->rm_call.cb_prog, msg->rm_call.cb_vers, msg->rm_call.cb_proc, uaddr); free(uaddr); } #endif *replyp = ent->cache_reply; *replylenp = ent->cache_replylen; (void) mutex_unlock(&dupreq_lock); return (1); } } /* * Failed to find entry * Remember a few things so we can do a set later */ uc->uc_proc = msg->rm_call.cb_proc; uc->uc_vers = msg->rm_call.cb_vers; uc->uc_prog = msg->rm_call.cb_prog; (void) mutex_unlock(&dupreq_lock); return (0); }