Index: head/lib/libc/rpc/rpc_generic.c =================================================================== --- head/lib/libc/rpc/rpc_generic.c (revision 319368) +++ head/lib/libc/rpc/rpc_generic.c (revision 319369) @@ -1,836 +1,844 @@ /* $NetBSD: rpc_generic.c,v 1.4 2000/09/28 09:07:04 kleink Exp $ */ /*- * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1986-1991 by Sun Microsystems Inc. */ /* #pragma ident "@(#)rpc_generic.c 1.17 94/04/24 SMI" */ #include __FBSDID("$FreeBSD$"); /* * rpc_generic.c, Miscl routines for RPC. * */ #include "namespace.h" #include "reentrant.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "rpc_com.h" #include "mt_misc.h" struct handle { NCONF_HANDLE *nhandle; int nflag; /* Whether NETPATH or NETCONFIG */ int nettype; }; static const struct _rpcnettype { const char *name; const int type; } _rpctypelist[] = { { "netpath", _RPC_NETPATH }, { "visible", _RPC_VISIBLE }, { "circuit_v", _RPC_CIRCUIT_V }, { "datagram_v", _RPC_DATAGRAM_V }, { "circuit_n", _RPC_CIRCUIT_N }, { "datagram_n", _RPC_DATAGRAM_N }, { "tcp", _RPC_TCP }, { "udp", _RPC_UDP }, { 0, _RPC_NONE } }; struct netid_af { const char *netid; int af; int protocol; }; static const struct netid_af na_cvt[] = { { "udp", AF_INET, IPPROTO_UDP }, { "tcp", AF_INET, IPPROTO_TCP }, #ifdef INET6 { "udp6", AF_INET6, IPPROTO_UDP }, { "tcp6", AF_INET6, IPPROTO_TCP }, #endif { "local", AF_LOCAL, 0 } }; #if 0 static char *strlocase(char *); #endif static int getnettype(const char *); /* * Cache the result of getrlimit(), so we don't have to do an * expensive call every time. */ int __rpc_dtbsize(void) { static int tbsize; struct rlimit rl; if (tbsize) { return (tbsize); } if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { return (tbsize = (int)rl.rlim_max); } /* * Something wrong. I'll try to save face by returning a * pessimistic number. */ return (32); } /* * Find the appropriate buffer size * * size - Size requested */ u_int /*ARGSUSED*/ __rpc_get_t_size(int af, int proto, int size) { int maxsize, defsize; maxsize = 256 * 1024; /* XXX */ switch (proto) { case IPPROTO_TCP: defsize = 64 * 1024; /* XXX */ break; case IPPROTO_UDP: defsize = UDPMSGSIZE; break; default: defsize = RPC_MAXDATASIZE; break; } if (size == 0) return defsize; /* Check whether the value is within the upper max limit */ return (size > maxsize ? (u_int)maxsize : (u_int)size); } /* * Find the appropriate address buffer size */ u_int __rpc_get_a_size(int af) { switch (af) { case AF_INET: return sizeof (struct sockaddr_in); #ifdef INET6 case AF_INET6: return sizeof (struct sockaddr_in6); #endif case AF_LOCAL: return sizeof (struct sockaddr_un); default: break; } return ((u_int)RPC_MAXADDRSIZE); } #if 0 static char * strlocase(char *p) { char *t = p; for (; *p; p++) if (isupper(*p)) *p = tolower(*p); return (t); } #endif /* * Returns the type of the network as defined in * If nettype is NULL, it defaults to NETPATH. */ static int getnettype(const char *nettype) { int i; if ((nettype == NULL) || (nettype[0] == 0)) { return (_RPC_NETPATH); /* Default */ } #if 0 nettype = strlocase(nettype); #endif for (i = 0; _rpctypelist[i].name; i++) if (strcasecmp(nettype, _rpctypelist[i].name) == 0) { return (_rpctypelist[i].type); } return (_rpctypelist[i].type); } static thread_key_t tcp_key, udp_key; static once_t keys_once = ONCE_INITIALIZER; static int tcp_key_error, udp_key_error; static void keys_init(void) { tcp_key_error = thr_keycreate(&tcp_key, free); udp_key_error = thr_keycreate(&udp_key, free); } /* * For the given nettype (tcp or udp only), return the first structure found. * This should be freed by calling freenetconfigent() */ struct netconfig * __rpc_getconfip(const char *nettype) { char *netid; char *netid_tcp = (char *) NULL; char *netid_udp = (char *) NULL; static char *netid_tcp_main; static char *netid_udp_main; struct netconfig *dummy; int main_thread; if ((main_thread = thr_main())) { netid_udp = netid_udp_main; netid_tcp = netid_tcp_main; } else { if (thr_once(&keys_once, keys_init) != 0 || tcp_key_error != 0 || udp_key_error != 0) return (NULL); netid_tcp = (char *)thr_getspecific(tcp_key); netid_udp = (char *)thr_getspecific(udp_key); } if (!netid_udp && !netid_tcp) { struct netconfig *nconf; void *confighandle; if (!(confighandle = setnetconfig())) { syslog (LOG_ERR, "rpc: failed to open " NETCONFIG); return (NULL); } while ((nconf = getnetconfig(confighandle)) != NULL) { if (strcmp(nconf->nc_protofmly, NC_INET) == 0) { if (strcmp(nconf->nc_proto, NC_TCP) == 0 && netid_tcp == NULL) { netid_tcp = strdup(nconf->nc_netid); if (main_thread) netid_tcp_main = netid_tcp; else thr_setspecific(tcp_key, (void *) netid_tcp); } else if (strcmp(nconf->nc_proto, NC_UDP) == 0 && netid_udp == NULL) { netid_udp = strdup(nconf->nc_netid); if (main_thread) netid_udp_main = netid_udp; else thr_setspecific(udp_key, (void *) netid_udp); } } } endnetconfig(confighandle); } if (strcmp(nettype, "udp") == 0) netid = netid_udp; else if (strcmp(nettype, "tcp") == 0) netid = netid_tcp; else { return (NULL); } if ((netid == NULL) || (netid[0] == 0)) { return (NULL); } dummy = getnetconfigent(netid); return (dummy); } /* * Returns the type of the nettype, which should then be used with * __rpc_getconf(). */ void * __rpc_setconf(const char *nettype) { struct handle *handle; handle = (struct handle *) malloc(sizeof (struct handle)); if (handle == NULL) { return (NULL); } switch (handle->nettype = getnettype(nettype)) { case _RPC_NETPATH: case _RPC_CIRCUIT_N: case _RPC_DATAGRAM_N: if (!(handle->nhandle = setnetpath())) goto failed; handle->nflag = TRUE; break; case _RPC_VISIBLE: case _RPC_CIRCUIT_V: case _RPC_DATAGRAM_V: case _RPC_TCP: case _RPC_UDP: if (!(handle->nhandle = setnetconfig())) { syslog (LOG_ERR, "rpc: failed to open " NETCONFIG); goto failed; } handle->nflag = FALSE; break; default: goto failed; } return (handle); failed: free(handle); return (NULL); } /* * Returns the next netconfig struct for the given "net" type. * __rpc_setconf() should have been called previously. */ struct netconfig * __rpc_getconf(void *vhandle) { struct handle *handle; struct netconfig *nconf; handle = (struct handle *)vhandle; if (handle == NULL) { return (NULL); } for (;;) { if (handle->nflag) nconf = getnetpath(handle->nhandle); else nconf = getnetconfig(handle->nhandle); if (nconf == NULL) break; if ((nconf->nc_semantics != NC_TPI_CLTS) && (nconf->nc_semantics != NC_TPI_COTS) && (nconf->nc_semantics != NC_TPI_COTS_ORD)) continue; switch (handle->nettype) { case _RPC_VISIBLE: if (!(nconf->nc_flag & NC_VISIBLE)) continue; /* FALLTHROUGH */ case _RPC_NETPATH: /* Be happy */ break; case _RPC_CIRCUIT_V: if (!(nconf->nc_flag & NC_VISIBLE)) continue; /* FALLTHROUGH */ case _RPC_CIRCUIT_N: if ((nconf->nc_semantics != NC_TPI_COTS) && (nconf->nc_semantics != NC_TPI_COTS_ORD)) continue; break; case _RPC_DATAGRAM_V: if (!(nconf->nc_flag & NC_VISIBLE)) continue; /* FALLTHROUGH */ case _RPC_DATAGRAM_N: if (nconf->nc_semantics != NC_TPI_CLTS) continue; break; case _RPC_TCP: if (((nconf->nc_semantics != NC_TPI_COTS) && (nconf->nc_semantics != NC_TPI_COTS_ORD)) || (strcmp(nconf->nc_protofmly, NC_INET) #ifdef INET6 && strcmp(nconf->nc_protofmly, NC_INET6)) #else ) #endif || strcmp(nconf->nc_proto, NC_TCP)) continue; break; case _RPC_UDP: if ((nconf->nc_semantics != NC_TPI_CLTS) || (strcmp(nconf->nc_protofmly, NC_INET) #ifdef INET6 && strcmp(nconf->nc_protofmly, NC_INET6)) #else ) #endif || strcmp(nconf->nc_proto, NC_UDP)) continue; break; } break; } return (nconf); } void __rpc_endconf(void *vhandle) { struct handle *handle; handle = (struct handle *) vhandle; if (handle == NULL) { return; } if (handle->nflag) { endnetpath(handle->nhandle); } else { endnetconfig(handle->nhandle); } free(handle); } /* * Used to ping the NULL procedure for clnt handle. * Returns NULL if fails, else a non-NULL pointer. */ void * rpc_nullproc(CLIENT *clnt) { struct timeval TIMEOUT = {25, 0}; if (clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *) clnt); } /* * Try all possible transports until * one succeeds in finding the netconf for the given fd. */ struct netconfig * __rpcgettp(int fd) { const char *netid; struct __rpc_sockinfo si; if (!__rpc_fd2sockinfo(fd, &si)) return NULL; if (!__rpc_sockinfo2netid(&si, &netid)) return NULL; /*LINTED const castaway*/ return getnetconfigent((char *)netid); } int __rpc_fd2sockinfo(int fd, struct __rpc_sockinfo *sip) { socklen_t len; int type, proto; struct sockaddr_storage ss; len = sizeof ss; if (_getsockname(fd, (struct sockaddr *)(void *)&ss, &len) < 0) return 0; sip->si_alen = len; len = sizeof type; if (_getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len) < 0) return 0; /* XXX */ if (ss.ss_family != AF_LOCAL) { if (type == SOCK_STREAM) proto = IPPROTO_TCP; else if (type == SOCK_DGRAM) proto = IPPROTO_UDP; else return 0; } else proto = 0; sip->si_af = ss.ss_family; sip->si_proto = proto; sip->si_socktype = type; return 1; } /* * Linear search, but the number of entries is small. */ int __rpc_nconf2sockinfo(const struct netconfig *nconf, struct __rpc_sockinfo *sip) { int i; for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++) if (strcmp(na_cvt[i].netid, nconf->nc_netid) == 0 || ( strcmp(nconf->nc_netid, "unix") == 0 && strcmp(na_cvt[i].netid, "local") == 0)) { sip->si_af = na_cvt[i].af; sip->si_proto = na_cvt[i].protocol; sip->si_socktype = __rpc_seman2socktype((int)nconf->nc_semantics); if (sip->si_socktype == -1) return 0; sip->si_alen = __rpc_get_a_size(sip->si_af); return 1; } return 0; } int __rpc_nconf2fd(const struct netconfig *nconf) { struct __rpc_sockinfo si; if (!__rpc_nconf2sockinfo(nconf, &si)) return 0; return _socket(si.si_af, si.si_socktype, si.si_proto); } int __rpc_sockinfo2netid(struct __rpc_sockinfo *sip, const char **netid) { int i; struct netconfig *nconf; nconf = getnetconfigent("local"); for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++) { if (na_cvt[i].af == sip->si_af && na_cvt[i].protocol == sip->si_proto) { if (strcmp(na_cvt[i].netid, "local") == 0 && nconf == NULL) { if (netid) *netid = "unix"; } else { if (netid) *netid = na_cvt[i].netid; } if (nconf != NULL) freenetconfigent(nconf); return 1; } } if (nconf != NULL) freenetconfigent(nconf); return 0; } char * taddr2uaddr(const struct netconfig *nconf, const struct netbuf *nbuf) { struct __rpc_sockinfo si; if (!__rpc_nconf2sockinfo(nconf, &si)) return NULL; return __rpc_taddr2uaddr_af(si.si_af, nbuf); } struct netbuf * uaddr2taddr(const struct netconfig *nconf, const char *uaddr) { struct __rpc_sockinfo si; if (!__rpc_nconf2sockinfo(nconf, &si)) return NULL; return __rpc_uaddr2taddr_af(si.si_af, uaddr); } char * __rpc_taddr2uaddr_af(int af, const struct netbuf *nbuf) { char *ret; struct sockaddr_in *sin; struct sockaddr_un *sun; char namebuf[INET_ADDRSTRLEN]; #ifdef INET6 struct sockaddr_in6 *sin6; char namebuf6[INET6_ADDRSTRLEN]; #endif u_int16_t port; switch (af) { case AF_INET: + if (nbuf->len < sizeof(*sin)) + return NULL; sin = nbuf->buf; if (inet_ntop(af, &sin->sin_addr, namebuf, sizeof namebuf) == NULL) return NULL; port = ntohs(sin->sin_port); if (asprintf(&ret, "%s.%u.%u", namebuf, ((u_int32_t)port) >> 8, port & 0xff) < 0) return NULL; break; #ifdef INET6 case AF_INET6: + if (nbuf->len < sizeof(*sin6)) + return NULL; sin6 = nbuf->buf; if (inet_ntop(af, &sin6->sin6_addr, namebuf6, sizeof namebuf6) == NULL) return NULL; port = ntohs(sin6->sin6_port); if (asprintf(&ret, "%s.%u.%u", namebuf6, ((u_int32_t)port) >> 8, port & 0xff) < 0) return NULL; break; #endif case AF_LOCAL: sun = nbuf->buf; if (asprintf(&ret, "%.*s", (int)(sun->sun_len - offsetof(struct sockaddr_un, sun_path)), sun->sun_path) < 0) return (NULL); break; default: return NULL; } return ret; } struct netbuf * __rpc_uaddr2taddr_af(int af, const char *uaddr) { struct netbuf *ret = NULL; char *addrstr, *p; unsigned port, portlo, porthi; struct sockaddr_in *sin; #ifdef INET6 struct sockaddr_in6 *sin6; #endif struct sockaddr_un *sun; port = 0; sin = NULL; + + if (uaddr == NULL) + return NULL; + addrstr = strdup(uaddr); if (addrstr == NULL) return NULL; /* * AF_LOCAL addresses are expected to be absolute * pathnames, anything else will be AF_INET or AF_INET6. */ if (*addrstr != '/') { p = strrchr(addrstr, '.'); if (p == NULL) goto out; portlo = (unsigned)atoi(p + 1); *p = '\0'; p = strrchr(addrstr, '.'); if (p == NULL) goto out; porthi = (unsigned)atoi(p + 1); *p = '\0'; port = (porthi << 8) | portlo; } ret = (struct netbuf *)malloc(sizeof *ret); if (ret == NULL) goto out; switch (af) { case AF_INET: sin = (struct sockaddr_in *)malloc(sizeof *sin); if (sin == NULL) goto out; memset(sin, 0, sizeof *sin); sin->sin_family = AF_INET; sin->sin_port = htons(port); if (inet_pton(AF_INET, addrstr, &sin->sin_addr) <= 0) { free(sin); free(ret); ret = NULL; goto out; } sin->sin_len = ret->maxlen = ret->len = sizeof *sin; ret->buf = sin; break; #ifdef INET6 case AF_INET6: sin6 = (struct sockaddr_in6 *)malloc(sizeof *sin6); if (sin6 == NULL) goto out; memset(sin6, 0, sizeof *sin6); sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(port); if (inet_pton(AF_INET6, addrstr, &sin6->sin6_addr) <= 0) { free(sin6); free(ret); ret = NULL; goto out; } sin6->sin6_len = ret->maxlen = ret->len = sizeof *sin6; ret->buf = sin6; break; #endif case AF_LOCAL: sun = (struct sockaddr_un *)malloc(sizeof *sun); if (sun == NULL) goto out; memset(sun, 0, sizeof *sun); sun->sun_family = AF_LOCAL; strncpy(sun->sun_path, addrstr, sizeof(sun->sun_path) - 1); ret->len = ret->maxlen = sun->sun_len = SUN_LEN(sun); ret->buf = sun; break; default: break; } out: free(addrstr); return ret; } int __rpc_seman2socktype(int semantics) { switch (semantics) { case NC_TPI_CLTS: return SOCK_DGRAM; case NC_TPI_COTS_ORD: return SOCK_STREAM; case NC_TPI_RAW: return SOCK_RAW; default: break; } return -1; } int __rpc_socktype2seman(int socktype) { switch (socktype) { case SOCK_DGRAM: return NC_TPI_CLTS; case SOCK_STREAM: return NC_TPI_COTS_ORD; case SOCK_RAW: return NC_TPI_RAW; default: break; } return -1; } /* * XXXX - IPv6 scope IDs can't be handled in universal addresses. * Here, we compare the original server address to that of the RPC * service we just received back from a call to rpcbind on the remote * machine. If they are both "link local" or "site local", copy * the scope id of the server address over to the service address. */ int __rpc_fixup_addr(struct netbuf *new, const struct netbuf *svc) { #ifdef INET6 struct sockaddr *sa_new, *sa_svc; struct sockaddr_in6 *sin6_new, *sin6_svc; sa_svc = (struct sockaddr *)svc->buf; sa_new = (struct sockaddr *)new->buf; if (sa_new->sa_family == sa_svc->sa_family && sa_new->sa_family == AF_INET6) { sin6_new = (struct sockaddr_in6 *)new->buf; sin6_svc = (struct sockaddr_in6 *)svc->buf; if ((IN6_IS_ADDR_LINKLOCAL(&sin6_new->sin6_addr) && IN6_IS_ADDR_LINKLOCAL(&sin6_svc->sin6_addr)) || (IN6_IS_ADDR_SITELOCAL(&sin6_new->sin6_addr) && IN6_IS_ADDR_SITELOCAL(&sin6_svc->sin6_addr))) { sin6_new->sin6_scope_id = sin6_svc->sin6_scope_id; } } #endif return 1; } int __rpc_sockisbound(int fd) { struct sockaddr_storage ss; socklen_t slen; slen = sizeof (struct sockaddr_storage); if (_getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) return 0; switch (ss.ss_family) { case AF_INET: return (((struct sockaddr_in *) (void *)&ss)->sin_port != 0); #ifdef INET6 case AF_INET6: return (((struct sockaddr_in6 *) (void *)&ss)->sin6_port != 0); #endif case AF_LOCAL: /* XXX check this */ return (((struct sockaddr_un *) (void *)&ss)->sun_path[0] != '\0'); default: break; } return 0; } Index: head/lib/libc/rpc/rpcb_prot.c =================================================================== --- head/lib/libc/rpc/rpcb_prot.c (revision 319368) +++ head/lib/libc/rpc/rpcb_prot.c (revision 319369) @@ -1,315 +1,316 @@ /* $NetBSD: rpcb_prot.c,v 1.3 2000/07/14 08:40:42 fvdl Exp $ */ /*- * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1986-1991 by Sun Microsystems Inc. */ /* #ident "@(#)rpcb_prot.c 1.13 94/04/24 SMI" */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rpcb_prot.c 1.9 89/04/21 Copyr 1984 Sun Micro"; #endif #include __FBSDID("$FreeBSD$"); /* * rpcb_prot.c * XDR routines for the rpcbinder version 3. * * Copyright (C) 1984, 1988, Sun Microsystems, Inc. */ #include "namespace.h" #include #include #include #include +#include #include "un-namespace.h" bool_t xdr_rpcb(XDR *xdrs, RPCB *objp) { if (!xdr_rpcprog(xdrs, &objp->r_prog)) { return (FALSE); } if (!xdr_rpcvers(xdrs, &objp->r_vers)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->r_netid, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_netid, RPC_MAXDATASIZE)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->r_addr, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_addr, RPC_MAXDATASIZE)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->r_owner, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_owner, RPC_MAXDATASIZE)) { return (FALSE); } return (TRUE); } /* * rpcblist_ptr implements a linked list. The RPCL definition from * rpcb_prot.x is: * * struct rpcblist { * rpcb rpcb_map; * struct rpcblist *rpcb_next; * }; * typedef rpcblist *rpcblist_ptr; * * Recall that "pointers" in XDR are encoded as a boolean, indicating whether * there's any data behind the pointer, followed by the data (if any exists). * The boolean can be interpreted as ``more data follows me''; if FALSE then * nothing follows the boolean; if TRUE then the boolean is followed by an * actual struct rpcb, and another rpcblist_ptr (declared in RPCL as "struct * rpcblist *"). * * This could be implemented via the xdr_pointer type, though this would * result in one recursive call per element in the list. Rather than do that * we can ``unwind'' the recursion into a while loop and use xdr_reference to * serialize the rpcb elements. */ bool_t xdr_rpcblist_ptr(XDR *xdrs, rpcblist_ptr *rp) { /* * more_elements is pre-computed in case the direction is * XDR_ENCODE or XDR_FREE. more_elements is overwritten by * xdr_bool when the direction is XDR_DECODE. */ bool_t more_elements; int freeing = (xdrs->x_op == XDR_FREE); rpcblist_ptr next; rpcblist_ptr next_copy; next = NULL; for (;;) { more_elements = (bool_t)(*rp != NULL); if (! xdr_bool(xdrs, &more_elements)) { return (FALSE); } if (! more_elements) { return (TRUE); /* we are done */ } /* * the unfortunate side effect of non-recursion is that in * the case of freeing we must remember the next object * before we free the current object ... */ if (freeing && *rp) next = (*rp)->rpcb_next; if (! xdr_reference(xdrs, (caddr_t *)rp, (u_int)sizeof (rpcblist), (xdrproc_t)xdr_rpcb)) { return (FALSE); } if (freeing) { next_copy = next; rp = &next_copy; /* * Note that in the subsequent iteration, next_copy * gets nulled out by the xdr_reference * but next itself survives. */ } else if (*rp) { rp = &((*rp)->rpcb_next); } } /*NOTREACHED*/ } /* * xdr_rpcblist() is specified to take a RPCBLIST **, but is identical in * functionality to xdr_rpcblist_ptr(). */ bool_t xdr_rpcblist(XDR *xdrs, RPCBLIST **rp) { bool_t dummy; dummy = xdr_rpcblist_ptr(xdrs, (rpcblist_ptr *)rp); return (dummy); } bool_t xdr_rpcb_entry(XDR *xdrs, rpcb_entry *objp) { - if (!xdr_string(xdrs, &objp->r_maddr, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_maddr, RPC_MAXDATASIZE)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->r_nc_netid, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_nc_netid, RPC_MAXDATASIZE)) { return (FALSE); } if (!xdr_u_int32_t(xdrs, &objp->r_nc_semantics)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->r_nc_protofmly, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_nc_protofmly, RPC_MAXDATASIZE)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->r_nc_proto, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_nc_proto, RPC_MAXDATASIZE)) { return (FALSE); } return (TRUE); } bool_t xdr_rpcb_entry_list_ptr(XDR *xdrs, rpcb_entry_list_ptr *rp) { /* * more_elements is pre-computed in case the direction is * XDR_ENCODE or XDR_FREE. more_elements is overwritten by * xdr_bool when the direction is XDR_DECODE. */ bool_t more_elements; int freeing = (xdrs->x_op == XDR_FREE); rpcb_entry_list_ptr next; rpcb_entry_list_ptr next_copy; next = NULL; for (;;) { more_elements = (bool_t)(*rp != NULL); if (! xdr_bool(xdrs, &more_elements)) { return (FALSE); } if (! more_elements) { return (TRUE); /* we are done */ } /* * the unfortunate side effect of non-recursion is that in * the case of freeing we must remember the next object * before we free the current object ... */ if (freeing && *rp) next = (*rp)->rpcb_entry_next; if (! xdr_reference(xdrs, (caddr_t *)rp, (u_int)sizeof (rpcb_entry_list), (xdrproc_t)xdr_rpcb_entry)) { return (FALSE); } if (freeing) { next_copy = next; rp = &next_copy; /* * Note that in the subsequent iteration, next_copy * gets nulled out by the xdr_reference * but next itself survives. */ } else if (*rp) { rp = &((*rp)->rpcb_entry_next); } } /*NOTREACHED*/ } /* * XDR remote call arguments * written for XDR_ENCODE direction only */ bool_t xdr_rpcb_rmtcallargs(XDR *xdrs, struct rpcb_rmtcallargs *p) { struct r_rpcb_rmtcallargs *objp = (struct r_rpcb_rmtcallargs *)(void *)p; u_int lenposition, argposition, position; int32_t *buf; buf = XDR_INLINE(xdrs, 3 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_rpcprog(xdrs, &objp->prog)) { return (FALSE); } if (!xdr_rpcvers(xdrs, &objp->vers)) { return (FALSE); } if (!xdr_rpcproc(xdrs, &objp->proc)) { return (FALSE); } } else { IXDR_PUT_U_INT32(buf, objp->prog); IXDR_PUT_U_INT32(buf, objp->vers); IXDR_PUT_U_INT32(buf, objp->proc); } /* * All the jugglery for just getting the size of the arguments */ lenposition = XDR_GETPOS(xdrs); if (! xdr_u_int(xdrs, &(objp->args.args_len))) { return (FALSE); } argposition = XDR_GETPOS(xdrs); if (! (*objp->xdr_args)(xdrs, objp->args.args_val)) { return (FALSE); } position = XDR_GETPOS(xdrs); objp->args.args_len = (u_int)((u_long)position - (u_long)argposition); XDR_SETPOS(xdrs, lenposition); if (! xdr_u_int(xdrs, &(objp->args.args_len))) { return (FALSE); } XDR_SETPOS(xdrs, position); return (TRUE); } /* * XDR remote call results * written for XDR_DECODE direction only */ bool_t xdr_rpcb_rmtcallres(XDR *xdrs, struct rpcb_rmtcallres *p) { bool_t dummy; struct r_rpcb_rmtcallres *objp = (struct r_rpcb_rmtcallres *)(void *)p; - if (!xdr_string(xdrs, &objp->addr, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->addr, RPC_MAXDATASIZE)) { return (FALSE); } if (!xdr_u_int(xdrs, &objp->results.results_len)) { return (FALSE); } dummy = (*(objp->xdr_res))(xdrs, objp->results.results_val); return (dummy); } bool_t xdr_netbuf(XDR *xdrs, struct netbuf *objp) { bool_t dummy; void **pp; if (!xdr_u_int32_t(xdrs, (u_int32_t *) &objp->maxlen)) { return (FALSE); } pp = &objp->buf; dummy = xdr_bytes(xdrs, (char **) pp, (u_int *)&(objp->len), objp->maxlen); return (dummy); } Index: head/lib/libc/rpc/rpcb_st_xdr.c =================================================================== --- head/lib/libc/rpc/rpcb_st_xdr.c (revision 319368) +++ head/lib/libc/rpc/rpcb_st_xdr.c (revision 319369) @@ -1,260 +1,261 @@ /* $NetBSD: rpcb_st_xdr.c,v 1.3 2000/07/14 08:40:42 fvdl Exp $ */ /*- * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright 1991 Sun Microsystems, Inc. * rpcb_stat_xdr.c */ /* * This file was generated from rpcb_prot.x, but includes only those * routines used with the rpcbind stats facility. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include +#include #include "un-namespace.h" /* Link list of all the stats about getport and getaddr */ bool_t xdr_rpcbs_addrlist(XDR *xdrs, rpcbs_addrlist *objp) { struct rpcbs_addrlist **pnext; if (!xdr_rpcprog(xdrs, &objp->prog)) { return (FALSE); } if (!xdr_rpcvers(xdrs, &objp->vers)) { return (FALSE); } if (!xdr_int(xdrs, &objp->success)) { return (FALSE); } if (!xdr_int(xdrs, &objp->failure)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->netid, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->netid, RPC_MAXDATASIZE)) { return (FALSE); } pnext = &objp->next; if (!xdr_pointer(xdrs, (char **) pnext, sizeof (rpcbs_addrlist), (xdrproc_t)xdr_rpcbs_addrlist)) { return (FALSE); } return (TRUE); } /* Link list of all the stats about rmtcall */ bool_t xdr_rpcbs_rmtcalllist(XDR *xdrs, rpcbs_rmtcalllist *objp) { int32_t *buf; struct rpcbs_rmtcalllist **pnext; if (xdrs->x_op == XDR_ENCODE) { buf = XDR_INLINE(xdrs, 6 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_rpcprog(xdrs, &objp->prog)) { return (FALSE); } if (!xdr_rpcvers(xdrs, &objp->vers)) { return (FALSE); } if (!xdr_rpcproc(xdrs, &objp->proc)) { return (FALSE); } if (!xdr_int(xdrs, &objp->success)) { return (FALSE); } if (!xdr_int(xdrs, &objp->failure)) { return (FALSE); } if (!xdr_int(xdrs, &objp->indirect)) { return (FALSE); } } else { IXDR_PUT_U_INT32(buf, objp->prog); IXDR_PUT_U_INT32(buf, objp->vers); IXDR_PUT_U_INT32(buf, objp->proc); IXDR_PUT_INT32(buf, objp->success); IXDR_PUT_INT32(buf, objp->failure); IXDR_PUT_INT32(buf, objp->indirect); } - if (!xdr_string(xdrs, &objp->netid, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->netid, RPC_MAXDATASIZE)) { return (FALSE); } pnext = &objp->next; if (!xdr_pointer(xdrs, (char **) pnext, sizeof (rpcbs_rmtcalllist), (xdrproc_t)xdr_rpcbs_rmtcalllist)) { return (FALSE); } return (TRUE); } else if (xdrs->x_op == XDR_DECODE) { buf = XDR_INLINE(xdrs, 6 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_rpcprog(xdrs, &objp->prog)) { return (FALSE); } if (!xdr_rpcvers(xdrs, &objp->vers)) { return (FALSE); } if (!xdr_rpcproc(xdrs, &objp->proc)) { return (FALSE); } if (!xdr_int(xdrs, &objp->success)) { return (FALSE); } if (!xdr_int(xdrs, &objp->failure)) { return (FALSE); } if (!xdr_int(xdrs, &objp->indirect)) { return (FALSE); } } else { objp->prog = (rpcprog_t)IXDR_GET_U_INT32(buf); objp->vers = (rpcvers_t)IXDR_GET_U_INT32(buf); objp->proc = (rpcproc_t)IXDR_GET_U_INT32(buf); objp->success = (int)IXDR_GET_INT32(buf); objp->failure = (int)IXDR_GET_INT32(buf); objp->indirect = (int)IXDR_GET_INT32(buf); } - if (!xdr_string(xdrs, &objp->netid, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->netid, RPC_MAXDATASIZE)) { return (FALSE); } if (!xdr_pointer(xdrs, (char **) pnext, sizeof (rpcbs_rmtcalllist), (xdrproc_t)xdr_rpcbs_rmtcalllist)) { return (FALSE); } return (TRUE); } if (!xdr_rpcprog(xdrs, &objp->prog)) { return (FALSE); } if (!xdr_rpcvers(xdrs, &objp->vers)) { return (FALSE); } if (!xdr_rpcproc(xdrs, &objp->proc)) { return (FALSE); } if (!xdr_int(xdrs, &objp->success)) { return (FALSE); } if (!xdr_int(xdrs, &objp->failure)) { return (FALSE); } if (!xdr_int(xdrs, &objp->indirect)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->netid, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->netid, RPC_MAXDATASIZE)) { return (FALSE); } if (!xdr_pointer(xdrs, (char **) pnext, sizeof (rpcbs_rmtcalllist), (xdrproc_t)xdr_rpcbs_rmtcalllist)) { return (FALSE); } return (TRUE); } bool_t xdr_rpcbs_proc(XDR *xdrs, rpcbs_proc objp) { if (!xdr_vector(xdrs, (char *)(void *)objp, RPCBSTAT_HIGHPROC, sizeof (int), (xdrproc_t)xdr_int)) { return (FALSE); } return (TRUE); } bool_t xdr_rpcbs_addrlist_ptr(XDR *xdrs, rpcbs_addrlist_ptr *objp) { if (!xdr_pointer(xdrs, (char **)objp, sizeof (rpcbs_addrlist), (xdrproc_t)xdr_rpcbs_addrlist)) { return (FALSE); } return (TRUE); } bool_t xdr_rpcbs_rmtcalllist_ptr(XDR *xdrs, rpcbs_rmtcalllist_ptr *objp) { if (!xdr_pointer(xdrs, (char **)objp, sizeof (rpcbs_rmtcalllist), (xdrproc_t)xdr_rpcbs_rmtcalllist)) { return (FALSE); } return (TRUE); } bool_t xdr_rpcb_stat(XDR *xdrs, rpcb_stat *objp) { if (!xdr_rpcbs_proc(xdrs, objp->info)) { return (FALSE); } if (!xdr_int(xdrs, &objp->setinfo)) { return (FALSE); } if (!xdr_int(xdrs, &objp->unsetinfo)) { return (FALSE); } if (!xdr_rpcbs_addrlist_ptr(xdrs, &objp->addrinfo)) { return (FALSE); } if (!xdr_rpcbs_rmtcalllist_ptr(xdrs, &objp->rmtinfo)) { return (FALSE); } return (TRUE); } /* * One rpcb_stat structure is returned for each version of rpcbind * being monitored. */ bool_t xdr_rpcb_stat_byvers(XDR *xdrs, rpcb_stat_byvers objp) { if (!xdr_vector(xdrs, (char *)(void *)objp, RPCBVERS_STAT, sizeof (rpcb_stat), (xdrproc_t)xdr_rpcb_stat)) { return (FALSE); } return (TRUE); } Index: head/lib/libc/xdr/xdr.c =================================================================== --- head/lib/libc/xdr/xdr.c (revision 319368) +++ head/lib/libc/xdr/xdr.c (revision 319369) @@ -1,907 +1,927 @@ /* $NetBSD: xdr.c,v 1.22 2000/07/06 03:10:35 christos Exp $ */ /*- * Copyright (c) 2010, Oracle America, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the "Oracle America, Inc." nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char *sccsid2 = "@(#)xdr.c 1.35 87/08/12"; static char *sccsid = "@(#)xdr.c 2.1 88/07/29 4.0 RPCSRC"; #endif #include __FBSDID("$FreeBSD$"); /* * xdr.c, Generic XDR routines implementation. * * These are the "generic" xdr routines used to serialize and de-serialize * most common data items. See xdr.h for more info on the interface to * xdr. */ #include "namespace.h" #include #include #include #include +#include +#include #include #include #include "un-namespace.h" typedef quad_t longlong_t; /* ANSI long long type */ typedef u_quad_t u_longlong_t; /* ANSI unsigned long long type */ /* * constants specific to the xdr "protocol" */ #define XDR_FALSE ((long) 0) #define XDR_TRUE ((long) 1) -#define LASTUNSIGNED ((u_int) 0-1) /* * for unit alignment */ static const char xdr_zero[BYTES_PER_XDR_UNIT] = { 0, 0, 0, 0 }; /* * Free a data structure using XDR * Not a filter, but a convenient utility nonetheless */ void xdr_free(xdrproc_t proc, void *objp) { XDR x; x.x_op = XDR_FREE; (*proc)(&x, objp); } /* * XDR nothing */ bool_t xdr_void(void) { return (TRUE); } /* * XDR integers */ bool_t xdr_int(XDR *xdrs, int *ip) { long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (long) *ip; return (XDR_PUTLONG(xdrs, &l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, &l)) { return (FALSE); } *ip = (int) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned integers */ bool_t xdr_u_int(XDR *xdrs, u_int *up) { u_long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (u_long) *up; return (XDR_PUTLONG(xdrs, (long *)&l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, (long *)&l)) { return (FALSE); } *up = (u_int) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR long integers * same as xdr_u_long - open coded to save a proc call! */ bool_t xdr_long(XDR *xdrs, long *lp) { switch (xdrs->x_op) { case XDR_ENCODE: return (XDR_PUTLONG(xdrs, lp)); case XDR_DECODE: return (XDR_GETLONG(xdrs, lp)); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned long integers * same as xdr_long - open coded to save a proc call! */ bool_t xdr_u_long(XDR *xdrs, u_long *ulp) { switch (xdrs->x_op) { case XDR_ENCODE: return (XDR_PUTLONG(xdrs, (long *)ulp)); case XDR_DECODE: return (XDR_GETLONG(xdrs, (long *)ulp)); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR 32-bit integers * same as xdr_u_int32_t - open coded to save a proc call! */ bool_t xdr_int32_t(XDR *xdrs, int32_t *int32_p) { long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (long) *int32_p; return (XDR_PUTLONG(xdrs, &l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, &l)) { return (FALSE); } *int32_p = (int32_t) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned 32-bit integers * same as xdr_int32_t - open coded to save a proc call! */ bool_t xdr_u_int32_t(XDR *xdrs, u_int32_t *u_int32_p) { u_long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (u_long) *u_int32_p; return (XDR_PUTLONG(xdrs, (long *)&l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, (long *)&l)) { return (FALSE); } *u_int32_p = (u_int32_t) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned 32-bit integers * same as xdr_int32_t - open coded to save a proc call! */ bool_t xdr_uint32_t(XDR *xdrs, uint32_t *u_int32_p) { u_long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (u_long) *u_int32_p; return (XDR_PUTLONG(xdrs, (long *)&l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, (long *)&l)) { return (FALSE); } *u_int32_p = (u_int32_t) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR short integers */ bool_t xdr_short(XDR *xdrs, short *sp) { long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (long) *sp; return (XDR_PUTLONG(xdrs, &l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, &l)) { return (FALSE); } *sp = (short) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned short integers */ bool_t xdr_u_short(XDR *xdrs, u_short *usp) { u_long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (u_long) *usp; return (XDR_PUTLONG(xdrs, (long *)&l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, (long *)&l)) { return (FALSE); } *usp = (u_short) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR 16-bit integers */ bool_t xdr_int16_t(XDR *xdrs, int16_t *int16_p) { long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (long) *int16_p; return (XDR_PUTLONG(xdrs, &l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, &l)) { return (FALSE); } *int16_p = (int16_t) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned 16-bit integers */ bool_t xdr_u_int16_t(XDR *xdrs, u_int16_t *u_int16_p) { u_long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (u_long) *u_int16_p; return (XDR_PUTLONG(xdrs, (long *)&l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, (long *)&l)) { return (FALSE); } *u_int16_p = (u_int16_t) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned 16-bit integers */ bool_t xdr_uint16_t(XDR *xdrs, uint16_t *u_int16_p) { u_long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (u_long) *u_int16_p; return (XDR_PUTLONG(xdrs, (long *)&l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, (long *)&l)) { return (FALSE); } *u_int16_p = (u_int16_t) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR a char */ bool_t xdr_char(XDR *xdrs, char *cp) { int i; i = (*cp); if (!xdr_int(xdrs, &i)) { return (FALSE); } *cp = i; return (TRUE); } /* * XDR an unsigned char */ bool_t xdr_u_char(XDR *xdrs, u_char *cp) { u_int u; u = (*cp); if (!xdr_u_int(xdrs, &u)) { return (FALSE); } *cp = u; return (TRUE); } /* * XDR booleans */ bool_t xdr_bool(XDR *xdrs, bool_t *bp) { long lb; switch (xdrs->x_op) { case XDR_ENCODE: lb = *bp ? XDR_TRUE : XDR_FALSE; return (XDR_PUTLONG(xdrs, &lb)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, &lb)) { return (FALSE); } *bp = (lb == XDR_FALSE) ? FALSE : TRUE; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR enumerations */ bool_t xdr_enum(XDR *xdrs, enum_t *ep) { enum sizecheck { SIZEVAL }; /* used to find the size of an enum */ /* * enums are treated as ints */ /* LINTED */ if (sizeof (enum sizecheck) == sizeof (long)) { return (xdr_long(xdrs, (long *)(void *)ep)); } else /* LINTED */ if (sizeof (enum sizecheck) == sizeof (int)) { return (xdr_int(xdrs, (int *)(void *)ep)); } else /* LINTED */ if (sizeof (enum sizecheck) == sizeof (short)) { return (xdr_short(xdrs, (short *)(void *)ep)); } else { return (FALSE); } } /* * XDR opaque data * Allows the specification of a fixed size sequence of opaque bytes. * cp points to the opaque object and cnt gives the byte length. */ bool_t xdr_opaque(XDR *xdrs, caddr_t cp, u_int cnt) { u_int rndup; static int crud[BYTES_PER_XDR_UNIT]; /* * if no data we are done */ if (cnt == 0) return (TRUE); /* * round byte count to full xdr units */ rndup = cnt % BYTES_PER_XDR_UNIT; if (rndup > 0) rndup = BYTES_PER_XDR_UNIT - rndup; if (xdrs->x_op == XDR_DECODE) { if (!XDR_GETBYTES(xdrs, cp, cnt)) { return (FALSE); } if (rndup == 0) return (TRUE); return (XDR_GETBYTES(xdrs, (caddr_t)(void *)crud, rndup)); } if (xdrs->x_op == XDR_ENCODE) { if (!XDR_PUTBYTES(xdrs, cp, cnt)) { return (FALSE); } if (rndup == 0) return (TRUE); return (XDR_PUTBYTES(xdrs, xdr_zero, rndup)); } if (xdrs->x_op == XDR_FREE) { return (TRUE); } return (FALSE); } /* * XDR counted bytes * *cpp is a pointer to the bytes, *sizep is the count. * If *cpp is NULL maxsize bytes are allocated */ bool_t xdr_bytes(XDR *xdrs, char **cpp, u_int *sizep, u_int maxsize) { char *sp = *cpp; /* sp is the actual string pointer */ u_int nodesize; + bool_t ret, allocated = FALSE; /* * first deal with the length since xdr bytes are counted */ if (! xdr_u_int(xdrs, sizep)) { return (FALSE); } nodesize = *sizep; if ((nodesize > maxsize) && (xdrs->x_op != XDR_FREE)) { return (FALSE); } /* * now deal with the actual bytes */ switch (xdrs->x_op) { case XDR_DECODE: if (nodesize == 0) { return (TRUE); } if (sp == NULL) { *cpp = sp = mem_alloc(nodesize); + allocated = TRUE; } if (sp == NULL) { warnx("xdr_bytes: out of memory"); return (FALSE); } /* FALLTHROUGH */ case XDR_ENCODE: - return (xdr_opaque(xdrs, sp, nodesize)); + ret = xdr_opaque(xdrs, sp, nodesize); + if ((xdrs->x_op == XDR_DECODE) && (ret == FALSE)) { + if (allocated == TRUE) { + free(sp); + *cpp = NULL; + } + } + return (ret); case XDR_FREE: if (sp != NULL) { mem_free(sp, nodesize); *cpp = NULL; } return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * Implemented here due to commonality of the object. */ bool_t xdr_netobj(XDR *xdrs, struct netobj *np) { return (xdr_bytes(xdrs, &np->n_bytes, &np->n_len, MAX_NETOBJ_SZ)); } /* * XDR a descriminated union * Support routine for discriminated unions. * You create an array of xdrdiscrim structures, terminated with * an entry with a null procedure pointer. The routine gets * the discriminant value and then searches the array of xdrdiscrims * looking for that value. It calls the procedure given in the xdrdiscrim * to handle the discriminant. If there is no specific routine a default * routine may be called. * If there is no specific or default routine an error is returned. */ bool_t xdr_union(XDR *xdrs, enum_t *dscmp, char *unp, const struct xdr_discrim *choices, xdrproc_t dfault) /* * XDR *xdrs; * enum_t *dscmp; // enum to decide which arm to work on * char *unp; // the union itself * const struct xdr_discrim *choices; // [value, xdr proc] for each arm * xdrproc_t dfault; // default xdr routine */ { enum_t dscm; /* * we deal with the discriminator; it's an enum */ if (! xdr_enum(xdrs, dscmp)) { return (FALSE); } dscm = *dscmp; /* * search choices for a value that matches the discriminator. * if we find one, execute the xdr routine for that value. */ for (; choices->proc != NULL_xdrproc_t; choices++) { if (choices->value == dscm) return ((*(choices->proc))(xdrs, unp)); } /* * no match - execute the default xdr routine if there is one */ return ((dfault == NULL_xdrproc_t) ? FALSE : (*dfault)(xdrs, unp)); } /* * Non-portable xdr primitives. * Care should be taken when moving these routines to new architectures. */ /* * XDR null terminated ASCII strings * xdr_string deals with "C strings" - arrays of bytes that are * terminated by a NULL character. The parameter cpp references a * pointer to storage; If the pointer is null, then the necessary * storage is allocated. The last parameter is the max allowed length * of the string as specified by a protocol. */ bool_t xdr_string(XDR *xdrs, char **cpp, u_int maxsize) { char *sp = *cpp; /* sp is the actual string pointer */ u_int size; u_int nodesize; + bool_t ret, allocated = FALSE; /* * first deal with the length since xdr strings are counted-strings */ switch (xdrs->x_op) { case XDR_FREE: if (sp == NULL) { return(TRUE); /* already free */ } /* FALLTHROUGH */ case XDR_ENCODE: size = strlen(sp); break; case XDR_DECODE: break; } if (! xdr_u_int(xdrs, &size)) { return (FALSE); } if (size > maxsize) { return (FALSE); } nodesize = size + 1; /* * now deal with the actual bytes */ switch (xdrs->x_op) { case XDR_DECODE: if (nodesize == 0) { return (TRUE); } - if (sp == NULL) + if (sp == NULL) { *cpp = sp = mem_alloc(nodesize); + allocated = TRUE; + } if (sp == NULL) { warnx("xdr_string: out of memory"); return (FALSE); } sp[size] = 0; /* FALLTHROUGH */ case XDR_ENCODE: - return (xdr_opaque(xdrs, sp, size)); + ret = xdr_opaque(xdrs, sp, size); + if ((xdrs->x_op == XDR_DECODE) && (ret == FALSE)) { + if (allocated == TRUE) { + free(sp); + *cpp = NULL; + } + } + return (ret); case XDR_FREE: mem_free(sp, nodesize); *cpp = NULL; return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * Wrapper for xdr_string that can be called directly from * routines like clnt_call */ bool_t xdr_wrapstring(XDR *xdrs, char **cpp) { - return xdr_string(xdrs, cpp, LASTUNSIGNED); + return xdr_string(xdrs, cpp, RPC_MAXDATASIZE); } /* * NOTE: xdr_hyper(), xdr_u_hyper(), xdr_longlong_t(), and xdr_u_longlong_t() * are in the "non-portable" section because they require that a `long long' * be a 64-bit type. * * --thorpej@netbsd.org, November 30, 1999 */ /* * XDR 64-bit integers */ bool_t xdr_int64_t(XDR *xdrs, int64_t *llp) { u_long ul[2]; switch (xdrs->x_op) { case XDR_ENCODE: ul[0] = (u_long)((u_int64_t)*llp >> 32) & 0xffffffff; ul[1] = (u_long)((u_int64_t)*llp) & 0xffffffff; if (XDR_PUTLONG(xdrs, (long *)&ul[0]) == FALSE) return (FALSE); return (XDR_PUTLONG(xdrs, (long *)&ul[1])); case XDR_DECODE: if (XDR_GETLONG(xdrs, (long *)&ul[0]) == FALSE) return (FALSE); if (XDR_GETLONG(xdrs, (long *)&ul[1]) == FALSE) return (FALSE); *llp = (int64_t) (((u_int64_t)ul[0] << 32) | ((u_int64_t)ul[1])); return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned 64-bit integers */ bool_t xdr_u_int64_t(XDR *xdrs, u_int64_t *ullp) { u_long ul[2]; switch (xdrs->x_op) { case XDR_ENCODE: ul[0] = (u_long)(*ullp >> 32) & 0xffffffff; ul[1] = (u_long)(*ullp) & 0xffffffff; if (XDR_PUTLONG(xdrs, (long *)&ul[0]) == FALSE) return (FALSE); return (XDR_PUTLONG(xdrs, (long *)&ul[1])); case XDR_DECODE: if (XDR_GETLONG(xdrs, (long *)&ul[0]) == FALSE) return (FALSE); if (XDR_GETLONG(xdrs, (long *)&ul[1]) == FALSE) return (FALSE); *ullp = (u_int64_t) (((u_int64_t)ul[0] << 32) | ((u_int64_t)ul[1])); return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned 64-bit integers */ bool_t xdr_uint64_t(XDR *xdrs, uint64_t *ullp) { u_long ul[2]; switch (xdrs->x_op) { case XDR_ENCODE: ul[0] = (u_long)(*ullp >> 32) & 0xffffffff; ul[1] = (u_long)(*ullp) & 0xffffffff; if (XDR_PUTLONG(xdrs, (long *)&ul[0]) == FALSE) return (FALSE); return (XDR_PUTLONG(xdrs, (long *)&ul[1])); case XDR_DECODE: if (XDR_GETLONG(xdrs, (long *)&ul[0]) == FALSE) return (FALSE); if (XDR_GETLONG(xdrs, (long *)&ul[1]) == FALSE) return (FALSE); *ullp = (u_int64_t) (((u_int64_t)ul[0] << 32) | ((u_int64_t)ul[1])); return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR hypers */ bool_t xdr_hyper(XDR *xdrs, longlong_t *llp) { /* * Don't bother open-coding this; it's a fair amount of code. Just * call xdr_int64_t(). */ return (xdr_int64_t(xdrs, (int64_t *)llp)); } /* * XDR unsigned hypers */ bool_t xdr_u_hyper(XDR *xdrs, u_longlong_t *ullp) { /* * Don't bother open-coding this; it's a fair amount of code. Just * call xdr_u_int64_t(). */ return (xdr_u_int64_t(xdrs, (u_int64_t *)ullp)); } /* * XDR longlong_t's */ bool_t xdr_longlong_t(XDR *xdrs, longlong_t *llp) { /* * Don't bother open-coding this; it's a fair amount of code. Just * call xdr_int64_t(). */ return (xdr_int64_t(xdrs, (int64_t *)llp)); } /* * XDR u_longlong_t's */ bool_t xdr_u_longlong_t(XDR *xdrs, u_longlong_t *ullp) { /* * Don't bother open-coding this; it's a fair amount of code. Just * call xdr_u_int64_t(). */ return (xdr_u_int64_t(xdrs, (u_int64_t *)ullp)); } Index: head/sys/rpc/rpc_generic.c =================================================================== --- head/sys/rpc/rpc_generic.c (revision 319368) +++ head/sys/rpc/rpc_generic.c (revision 319369) @@ -1,878 +1,886 @@ /* $NetBSD: rpc_generic.c,v 1.4 2000/09/28 09:07:04 kleink Exp $ */ /*- * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1986-1991 by Sun Microsystems Inc. */ /* #pragma ident "@(#)rpc_generic.c 1.17 94/04/24 SMI" */ #include __FBSDID("$FreeBSD$"); /* * rpc_generic.c, Miscl routines for RPC. * */ #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern u_long sb_max_adj; /* not defined in socketvar.h */ #if __FreeBSD_version < 700000 #define strrchr rindex #endif /* Provide an entry point hook for the rpcsec_gss module. */ struct rpc_gss_entries rpc_gss_entries; struct handle { NCONF_HANDLE *nhandle; int nflag; /* Whether NETPATH or NETCONFIG */ int nettype; }; static const struct _rpcnettype { const char *name; const int type; } _rpctypelist[] = { { "netpath", _RPC_NETPATH }, { "visible", _RPC_VISIBLE }, { "circuit_v", _RPC_CIRCUIT_V }, { "datagram_v", _RPC_DATAGRAM_V }, { "circuit_n", _RPC_CIRCUIT_N }, { "datagram_n", _RPC_DATAGRAM_N }, { "tcp", _RPC_TCP }, { "udp", _RPC_UDP }, { 0, _RPC_NONE } }; struct netid_af { const char *netid; int af; int protocol; }; static const struct netid_af na_cvt[] = { { "udp", AF_INET, IPPROTO_UDP }, { "tcp", AF_INET, IPPROTO_TCP }, #ifdef INET6 { "udp6", AF_INET6, IPPROTO_UDP }, { "tcp6", AF_INET6, IPPROTO_TCP }, #endif { "local", AF_LOCAL, 0 } }; struct rpc_createerr rpc_createerr; /* * Find the appropriate buffer size */ u_int /*ARGSUSED*/ __rpc_get_t_size(int af, int proto, int size) { int defsize; switch (proto) { case IPPROTO_TCP: defsize = 64 * 1024; /* XXX */ break; case IPPROTO_UDP: defsize = UDPMSGSIZE; break; default: defsize = RPC_MAXDATASIZE; break; } if (size == 0) return defsize; /* Check whether the value is within the upper max limit */ return (size > sb_max_adj ? (u_int)sb_max_adj : (u_int)size); } /* * Find the appropriate address buffer size */ u_int __rpc_get_a_size(af) int af; { switch (af) { case AF_INET: return sizeof (struct sockaddr_in); #ifdef INET6 case AF_INET6: return sizeof (struct sockaddr_in6); #endif case AF_LOCAL: return sizeof (struct sockaddr_un); default: break; } return ((u_int)RPC_MAXADDRSIZE); } #if 0 /* * Used to ping the NULL procedure for clnt handle. * Returns NULL if fails, else a non-NULL pointer. */ void * rpc_nullproc(clnt) CLIENT *clnt; { struct timeval TIMEOUT = {25, 0}; if (clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *) clnt); } #endif int __rpc_socket2sockinfo(struct socket *so, struct __rpc_sockinfo *sip) { int type, proto; struct sockaddr *sa; sa_family_t family; struct sockopt opt; int error; CURVNET_SET(so->so_vnet); error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa); CURVNET_RESTORE(); if (error) return 0; sip->si_alen = sa->sa_len; family = sa->sa_family; free(sa, M_SONAME); opt.sopt_dir = SOPT_GET; opt.sopt_level = SOL_SOCKET; opt.sopt_name = SO_TYPE; opt.sopt_val = &type; opt.sopt_valsize = sizeof type; opt.sopt_td = NULL; error = sogetopt(so, &opt); if (error) return 0; /* XXX */ if (family != AF_LOCAL) { if (type == SOCK_STREAM) proto = IPPROTO_TCP; else if (type == SOCK_DGRAM) proto = IPPROTO_UDP; else return 0; } else proto = 0; sip->si_af = family; sip->si_proto = proto; sip->si_socktype = type; return 1; } /* * Linear search, but the number of entries is small. */ int __rpc_nconf2sockinfo(const struct netconfig *nconf, struct __rpc_sockinfo *sip) { int i; for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++) if (strcmp(na_cvt[i].netid, nconf->nc_netid) == 0 || ( strcmp(nconf->nc_netid, "unix") == 0 && strcmp(na_cvt[i].netid, "local") == 0)) { sip->si_af = na_cvt[i].af; sip->si_proto = na_cvt[i].protocol; sip->si_socktype = __rpc_seman2socktype((int)nconf->nc_semantics); if (sip->si_socktype == -1) return 0; sip->si_alen = __rpc_get_a_size(sip->si_af); return 1; } return 0; } struct socket * __rpc_nconf2socket(const struct netconfig *nconf) { struct __rpc_sockinfo si; struct socket *so; int error; if (!__rpc_nconf2sockinfo(nconf, &si)) return 0; so = NULL; error = socreate(si.si_af, &so, si.si_socktype, si.si_proto, curthread->td_ucred, curthread); if (error) return NULL; else return so; } char * taddr2uaddr(const struct netconfig *nconf, const struct netbuf *nbuf) { struct __rpc_sockinfo si; if (!__rpc_nconf2sockinfo(nconf, &si)) return NULL; return __rpc_taddr2uaddr_af(si.si_af, nbuf); } struct netbuf * uaddr2taddr(const struct netconfig *nconf, const char *uaddr) { struct __rpc_sockinfo si; if (!__rpc_nconf2sockinfo(nconf, &si)) return NULL; return __rpc_uaddr2taddr_af(si.si_af, uaddr); } char * __rpc_taddr2uaddr_af(int af, const struct netbuf *nbuf) { char *ret; struct sbuf sb; struct sockaddr_in *sin; struct sockaddr_un *sun; char namebuf[INET_ADDRSTRLEN]; #ifdef INET6 struct sockaddr_in6 *sin6; char namebuf6[INET6_ADDRSTRLEN]; #endif u_int16_t port; sbuf_new(&sb, NULL, 0, SBUF_AUTOEXTEND); switch (af) { case AF_INET: + if (nbuf->len < sizeof(*sin)) + return NULL; sin = nbuf->buf; if (inet_ntop(af, &sin->sin_addr, namebuf, sizeof namebuf) == NULL) return NULL; port = ntohs(sin->sin_port); if (sbuf_printf(&sb, "%s.%u.%u", namebuf, ((uint32_t)port) >> 8, port & 0xff) < 0) return NULL; break; #ifdef INET6 case AF_INET6: + if (nbuf->len < sizeof(*sin6)) + return NULL; sin6 = nbuf->buf; if (inet_ntop(af, &sin6->sin6_addr, namebuf6, sizeof namebuf6) == NULL) return NULL; port = ntohs(sin6->sin6_port); if (sbuf_printf(&sb, "%s.%u.%u", namebuf6, ((uint32_t)port) >> 8, port & 0xff) < 0) return NULL; break; #endif case AF_LOCAL: sun = nbuf->buf; if (sbuf_printf(&sb, "%.*s", (int)(sun->sun_len - offsetof(struct sockaddr_un, sun_path)), sun->sun_path) < 0) return (NULL); break; default: return NULL; } sbuf_finish(&sb); ret = strdup(sbuf_data(&sb), M_RPC); sbuf_delete(&sb); return ret; } struct netbuf * __rpc_uaddr2taddr_af(int af, const char *uaddr) { struct netbuf *ret = NULL; char *addrstr, *p; unsigned port, portlo, porthi; struct sockaddr_in *sin; #ifdef INET6 struct sockaddr_in6 *sin6; #endif struct sockaddr_un *sun; port = 0; sin = NULL; + + if (uaddr == NULL) + return NULL; + addrstr = strdup(uaddr, M_RPC); if (addrstr == NULL) return NULL; /* * AF_LOCAL addresses are expected to be absolute * pathnames, anything else will be AF_INET or AF_INET6. */ if (*addrstr != '/') { p = strrchr(addrstr, '.'); if (p == NULL) goto out; portlo = (unsigned)strtol(p + 1, NULL, 10); *p = '\0'; p = strrchr(addrstr, '.'); if (p == NULL) goto out; porthi = (unsigned)strtol(p + 1, NULL, 10); *p = '\0'; port = (porthi << 8) | portlo; } ret = (struct netbuf *)malloc(sizeof *ret, M_RPC, M_WAITOK); switch (af) { case AF_INET: sin = (struct sockaddr_in *)malloc(sizeof *sin, M_RPC, M_WAITOK); memset(sin, 0, sizeof *sin); sin->sin_family = AF_INET; sin->sin_port = htons(port); if (inet_pton(AF_INET, addrstr, &sin->sin_addr) <= 0) { free(sin, M_RPC); free(ret, M_RPC); ret = NULL; goto out; } sin->sin_len = ret->maxlen = ret->len = sizeof *sin; ret->buf = sin; break; #ifdef INET6 case AF_INET6: sin6 = (struct sockaddr_in6 *)malloc(sizeof *sin6, M_RPC, M_WAITOK); memset(sin6, 0, sizeof *sin6); sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(port); if (inet_pton(AF_INET6, addrstr, &sin6->sin6_addr) <= 0) { free(sin6, M_RPC); free(ret, M_RPC); ret = NULL; goto out; } sin6->sin6_len = ret->maxlen = ret->len = sizeof *sin6; ret->buf = sin6; break; #endif case AF_LOCAL: sun = (struct sockaddr_un *)malloc(sizeof *sun, M_RPC, M_WAITOK); memset(sun, 0, sizeof *sun); sun->sun_family = AF_LOCAL; strncpy(sun->sun_path, addrstr, sizeof(sun->sun_path) - 1); ret->len = ret->maxlen = sun->sun_len = SUN_LEN(sun); ret->buf = sun; break; default: break; } out: free(addrstr, M_RPC); return ret; } int __rpc_seman2socktype(int semantics) { switch (semantics) { case NC_TPI_CLTS: return SOCK_DGRAM; case NC_TPI_COTS_ORD: return SOCK_STREAM; case NC_TPI_RAW: return SOCK_RAW; default: break; } return -1; } int __rpc_socktype2seman(int socktype) { switch (socktype) { case SOCK_DGRAM: return NC_TPI_CLTS; case SOCK_STREAM: return NC_TPI_COTS_ORD; case SOCK_RAW: return NC_TPI_RAW; default: break; } return -1; } /* * Returns the type of the network as defined in * If nettype is NULL, it defaults to NETPATH. */ static int getnettype(const char *nettype) { int i; if ((nettype == NULL) || (nettype[0] == 0)) { return (_RPC_NETPATH); /* Default */ } #if 0 nettype = strlocase(nettype); #endif for (i = 0; _rpctypelist[i].name; i++) if (strcasecmp(nettype, _rpctypelist[i].name) == 0) { return (_rpctypelist[i].type); } return (_rpctypelist[i].type); } /* * For the given nettype (tcp or udp only), return the first structure found. * This should be freed by calling freenetconfigent() */ struct netconfig * __rpc_getconfip(const char *nettype) { char *netid; static char *netid_tcp = (char *) NULL; static char *netid_udp = (char *) NULL; struct netconfig *dummy; if (!netid_udp && !netid_tcp) { struct netconfig *nconf; void *confighandle; if (!(confighandle = setnetconfig())) { log(LOG_ERR, "rpc: failed to open " NETCONFIG); return (NULL); } while ((nconf = getnetconfig(confighandle)) != NULL) { if (strcmp(nconf->nc_protofmly, NC_INET) == 0) { if (strcmp(nconf->nc_proto, NC_TCP) == 0) { netid_tcp = strdup(nconf->nc_netid, M_RPC); } else if (strcmp(nconf->nc_proto, NC_UDP) == 0) { netid_udp = strdup(nconf->nc_netid, M_RPC); } } } endnetconfig(confighandle); } if (strcmp(nettype, "udp") == 0) netid = netid_udp; else if (strcmp(nettype, "tcp") == 0) netid = netid_tcp; else { return (NULL); } if ((netid == NULL) || (netid[0] == 0)) { return (NULL); } dummy = getnetconfigent(netid); return (dummy); } /* * Returns the type of the nettype, which should then be used with * __rpc_getconf(). * * For simplicity in the kernel, we don't support the NETPATH * environment variable. We behave as userland would then NETPATH is * unset, i.e. iterate over all visible entries in netconfig. */ void * __rpc_setconf(nettype) const char *nettype; { struct handle *handle; handle = (struct handle *) malloc(sizeof (struct handle), M_RPC, M_WAITOK); switch (handle->nettype = getnettype(nettype)) { case _RPC_NETPATH: case _RPC_CIRCUIT_N: case _RPC_DATAGRAM_N: if (!(handle->nhandle = setnetconfig())) goto failed; handle->nflag = TRUE; break; case _RPC_VISIBLE: case _RPC_CIRCUIT_V: case _RPC_DATAGRAM_V: case _RPC_TCP: case _RPC_UDP: if (!(handle->nhandle = setnetconfig())) { log(LOG_ERR, "rpc: failed to open " NETCONFIG); goto failed; } handle->nflag = FALSE; break; default: goto failed; } return (handle); failed: free(handle, M_RPC); return (NULL); } /* * Returns the next netconfig struct for the given "net" type. * __rpc_setconf() should have been called previously. */ struct netconfig * __rpc_getconf(void *vhandle) { struct handle *handle; struct netconfig *nconf; handle = (struct handle *)vhandle; if (handle == NULL) { return (NULL); } for (;;) { if (handle->nflag) { nconf = getnetconfig(handle->nhandle); if (nconf && !(nconf->nc_flag & NC_VISIBLE)) continue; } else { nconf = getnetconfig(handle->nhandle); } if (nconf == NULL) break; if ((nconf->nc_semantics != NC_TPI_CLTS) && (nconf->nc_semantics != NC_TPI_COTS) && (nconf->nc_semantics != NC_TPI_COTS_ORD)) continue; switch (handle->nettype) { case _RPC_VISIBLE: if (!(nconf->nc_flag & NC_VISIBLE)) continue; /* FALLTHROUGH */ case _RPC_NETPATH: /* Be happy */ break; case _RPC_CIRCUIT_V: if (!(nconf->nc_flag & NC_VISIBLE)) continue; /* FALLTHROUGH */ case _RPC_CIRCUIT_N: if ((nconf->nc_semantics != NC_TPI_COTS) && (nconf->nc_semantics != NC_TPI_COTS_ORD)) continue; break; case _RPC_DATAGRAM_V: if (!(nconf->nc_flag & NC_VISIBLE)) continue; /* FALLTHROUGH */ case _RPC_DATAGRAM_N: if (nconf->nc_semantics != NC_TPI_CLTS) continue; break; case _RPC_TCP: if (((nconf->nc_semantics != NC_TPI_COTS) && (nconf->nc_semantics != NC_TPI_COTS_ORD)) || (strcmp(nconf->nc_protofmly, NC_INET) #ifdef INET6 && strcmp(nconf->nc_protofmly, NC_INET6)) #else ) #endif || strcmp(nconf->nc_proto, NC_TCP)) continue; break; case _RPC_UDP: if ((nconf->nc_semantics != NC_TPI_CLTS) || (strcmp(nconf->nc_protofmly, NC_INET) #ifdef INET6 && strcmp(nconf->nc_protofmly, NC_INET6)) #else ) #endif || strcmp(nconf->nc_proto, NC_UDP)) continue; break; } break; } return (nconf); } void __rpc_endconf(vhandle) void * vhandle; { struct handle *handle; handle = (struct handle *) vhandle; if (handle == NULL) { return; } endnetconfig(handle->nhandle); free(handle, M_RPC); } int __rpc_sockisbound(struct socket *so) { struct sockaddr *sa; int error, bound; CURVNET_SET(so->so_vnet); error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa); CURVNET_RESTORE(); if (error) return (0); switch (sa->sa_family) { case AF_INET: bound = (((struct sockaddr_in *) sa)->sin_port != 0); break; #ifdef INET6 case AF_INET6: bound = (((struct sockaddr_in6 *) sa)->sin6_port != 0); break; #endif case AF_LOCAL: /* XXX check this */ bound = (((struct sockaddr_un *) sa)->sun_path[0] != '\0'); break; default: bound = FALSE; break; } free(sa, M_SONAME); return bound; } /* * Implement XDR-style API for RPC call. */ enum clnt_stat clnt_call_private( CLIENT *cl, /* client handle */ struct rpc_callextra *ext, /* call metadata */ rpcproc_t proc, /* procedure number */ xdrproc_t xargs, /* xdr routine for args */ void *argsp, /* pointer to args */ xdrproc_t xresults, /* xdr routine for results */ void *resultsp, /* pointer to results */ struct timeval utimeout) /* seconds to wait before giving up */ { XDR xdrs; struct mbuf *mreq; struct mbuf *mrep; enum clnt_stat stat; mreq = m_getcl(M_WAITOK, MT_DATA, 0); xdrmbuf_create(&xdrs, mreq, XDR_ENCODE); if (!xargs(&xdrs, argsp)) { m_freem(mreq); return (RPC_CANTENCODEARGS); } XDR_DESTROY(&xdrs); stat = CLNT_CALL_MBUF(cl, ext, proc, mreq, &mrep, utimeout); m_freem(mreq); if (stat == RPC_SUCCESS) { xdrmbuf_create(&xdrs, mrep, XDR_DECODE); if (!xresults(&xdrs, resultsp)) { XDR_DESTROY(&xdrs); return (RPC_CANTDECODERES); } XDR_DESTROY(&xdrs); } return (stat); } /* * Bind a socket to a privileged IP port */ int bindresvport(struct socket *so, struct sockaddr *sa) { int old, error, af; bool_t freesa = FALSE; struct sockaddr_in *sin; #ifdef INET6 struct sockaddr_in6 *sin6; #endif struct sockopt opt; int proto, portrange, portlow; u_int16_t *portp; socklen_t salen; if (sa == NULL) { CURVNET_SET(so->so_vnet); error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa); CURVNET_RESTORE(); if (error) return (error); freesa = TRUE; af = sa->sa_family; salen = sa->sa_len; memset(sa, 0, sa->sa_len); } else { af = sa->sa_family; salen = sa->sa_len; } switch (af) { case AF_INET: proto = IPPROTO_IP; portrange = IP_PORTRANGE; portlow = IP_PORTRANGE_LOW; sin = (struct sockaddr_in *)sa; portp = &sin->sin_port; break; #ifdef INET6 case AF_INET6: proto = IPPROTO_IPV6; portrange = IPV6_PORTRANGE; portlow = IPV6_PORTRANGE_LOW; sin6 = (struct sockaddr_in6 *)sa; portp = &sin6->sin6_port; break; #endif default: return (EPFNOSUPPORT); } sa->sa_family = af; sa->sa_len = salen; if (*portp == 0) { bzero(&opt, sizeof(opt)); opt.sopt_dir = SOPT_GET; opt.sopt_level = proto; opt.sopt_name = portrange; opt.sopt_val = &old; opt.sopt_valsize = sizeof(old); error = sogetopt(so, &opt); if (error) { goto out; } opt.sopt_dir = SOPT_SET; opt.sopt_val = &portlow; error = sosetopt(so, &opt); if (error) goto out; } error = sobind(so, sa, curthread); if (*portp == 0) { if (error) { opt.sopt_dir = SOPT_SET; opt.sopt_val = &old; sosetopt(so, &opt); } } out: if (freesa) free(sa, M_SONAME); return (error); } /* * Kernel module glue */ static int krpc_modevent(module_t mod, int type, void *data) { return (0); } static moduledata_t krpc_mod = { "krpc", krpc_modevent, NULL, }; DECLARE_MODULE(krpc, krpc_mod, SI_SUB_VFS, SI_ORDER_ANY); /* So that loader and kldload(2) can find us, wherever we are.. */ MODULE_VERSION(krpc, 1); Index: head/sys/rpc/rpcb_clnt.c =================================================================== --- head/sys/rpc/rpcb_clnt.c (revision 319368) +++ head/sys/rpc/rpcb_clnt.c (revision 319369) @@ -1,1364 +1,1369 @@ /* $NetBSD: rpcb_clnt.c,v 1.6 2000/07/16 06:41:43 itojun Exp $ */ /*- * Copyright (c) 2010, Oracle America, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the "Oracle America, Inc." nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1986-1991 by Sun Microsystems Inc. */ /* #ident "@(#)rpcb_clnt.c 1.27 94/04/24 SMI" */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rpcb_clnt.c 1.30 89/06/21 Copyr 1988 Sun Micro"; #endif #include __FBSDID("$FreeBSD$"); /* * rpcb_clnt.c * interface to rpcbind rpc service. * * Copyright (C) 1988, Sun Microsystems, Inc. */ #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include static struct timeval tottimeout = { 60, 0 }; static const char nullstring[] = "\000"; static CLIENT *local_rpcb(void); #if 0 static const struct timeval rmttimeout = { 3, 0 }; static struct timeval rpcbrmttime = { 15, 0 }; #define CACHESIZE 6 struct address_cache { char *ac_host; char *ac_netid; char *ac_uaddr; struct netbuf *ac_taddr; struct address_cache *ac_next; }; static struct address_cache *front; static int cachesize; #define CLCR_GET_RPCB_TIMEOUT 1 #define CLCR_SET_RPCB_TIMEOUT 2 extern int __rpc_lowvers; static struct address_cache *check_cache(const char *, const char *); static void delete_cache(struct netbuf *); static void add_cache(const char *, const char *, struct netbuf *, char *); static CLIENT *getclnthandle(const char *, const struct netconfig *, char **); static CLIENT *local_rpcb(void); static struct netbuf *got_entry(rpcb_entry_list_ptr, const struct netconfig *); /* * This routine adjusts the timeout used for calls to the remote rpcbind. * Also, this routine can be used to set the use of portmapper version 2 * only when doing rpc_broadcasts * These are private routines that may not be provided in future releases. */ bool_t __rpc_control(request, info) int request; void *info; { switch (request) { case CLCR_GET_RPCB_TIMEOUT: *(struct timeval *)info = tottimeout; break; case CLCR_SET_RPCB_TIMEOUT: tottimeout = *(struct timeval *)info; break; case CLCR_SET_LOWVERS: __rpc_lowvers = *(int *)info; break; case CLCR_GET_LOWVERS: *(int *)info = __rpc_lowvers; break; default: return (FALSE); } return (TRUE); } /* * It might seem that a reader/writer lock would be more reasonable here. * However because getclnthandle(), the only user of the cache functions, * may do a delete_cache() operation if a check_cache() fails to return an * address useful to clnt_tli_create(), we may as well use a mutex. */ /* * As it turns out, if the cache lock is *not* a reader/writer lock, we will * block all clnt_create's if we are trying to connect to a host that's down, * since the lock will be held all during that time. */ /* * The routines check_cache(), add_cache(), delete_cache() manage the * cache of rpcbind addresses for (host, netid). */ static struct address_cache * check_cache(host, netid) const char *host, *netid; { struct address_cache *cptr; /* READ LOCK HELD ON ENTRY: rpcbaddr_cache_lock */ for (cptr = front; cptr != NULL; cptr = cptr->ac_next) { if (!strcmp(cptr->ac_host, host) && !strcmp(cptr->ac_netid, netid)) { #ifdef ND_DEBUG fprintf(stderr, "Found cache entry for %s: %s\n", host, netid); #endif return (cptr); } } return ((struct address_cache *) NULL); } static void delete_cache(addr) struct netbuf *addr; { struct address_cache *cptr, *prevptr = NULL; /* WRITE LOCK HELD ON ENTRY: rpcbaddr_cache_lock */ for (cptr = front; cptr != NULL; cptr = cptr->ac_next) { if (!memcmp(cptr->ac_taddr->buf, addr->buf, addr->len)) { free(cptr->ac_host); free(cptr->ac_netid); free(cptr->ac_taddr->buf); free(cptr->ac_taddr); if (cptr->ac_uaddr) free(cptr->ac_uaddr); if (prevptr) prevptr->ac_next = cptr->ac_next; else front = cptr->ac_next; free(cptr); cachesize--; break; } prevptr = cptr; } } static void add_cache(host, netid, taddr, uaddr) const char *host, *netid; char *uaddr; struct netbuf *taddr; { struct address_cache *ad_cache, *cptr, *prevptr; ad_cache = (struct address_cache *) malloc(sizeof (struct address_cache)); if (!ad_cache) { return; } ad_cache->ac_host = strdup(host); ad_cache->ac_netid = strdup(netid); ad_cache->ac_uaddr = uaddr ? strdup(uaddr) : NULL; ad_cache->ac_taddr = (struct netbuf *)malloc(sizeof (struct netbuf)); if (!ad_cache->ac_host || !ad_cache->ac_netid || !ad_cache->ac_taddr || (uaddr && !ad_cache->ac_uaddr)) { goto out; } ad_cache->ac_taddr->len = ad_cache->ac_taddr->maxlen = taddr->len; ad_cache->ac_taddr->buf = (char *) malloc(taddr->len); if (ad_cache->ac_taddr->buf == NULL) { out: if (ad_cache->ac_host) free(ad_cache->ac_host); if (ad_cache->ac_netid) free(ad_cache->ac_netid); if (ad_cache->ac_uaddr) free(ad_cache->ac_uaddr); if (ad_cache->ac_taddr) free(ad_cache->ac_taddr); free(ad_cache); return; } memcpy(ad_cache->ac_taddr->buf, taddr->buf, taddr->len); #ifdef ND_DEBUG fprintf(stderr, "Added to cache: %s : %s\n", host, netid); #endif /* VARIABLES PROTECTED BY rpcbaddr_cache_lock: cptr */ rwlock_wrlock(&rpcbaddr_cache_lock); if (cachesize < CACHESIZE) { ad_cache->ac_next = front; front = ad_cache; cachesize++; } else { /* Free the last entry */ cptr = front; prevptr = NULL; while (cptr->ac_next) { prevptr = cptr; cptr = cptr->ac_next; } #ifdef ND_DEBUG fprintf(stderr, "Deleted from cache: %s : %s\n", cptr->ac_host, cptr->ac_netid); #endif free(cptr->ac_host); free(cptr->ac_netid); free(cptr->ac_taddr->buf); free(cptr->ac_taddr); if (cptr->ac_uaddr) free(cptr->ac_uaddr); if (prevptr) { prevptr->ac_next = NULL; ad_cache->ac_next = front; front = ad_cache; } else { front = ad_cache; ad_cache->ac_next = NULL; } free(cptr); } rwlock_unlock(&rpcbaddr_cache_lock); } /* * This routine will return a client handle that is connected to the * rpcbind. If targaddr is non-NULL, the "universal address" of the * host will be stored in *targaddr; the caller is responsible for * freeing this string. * On error, returns NULL and free's everything. */ static CLIENT * getclnthandle(host, nconf, targaddr) const char *host; const struct netconfig *nconf; char **targaddr; { CLIENT *client; struct netbuf *addr, taddr; struct netbuf addr_to_delete; struct __rpc_sockinfo si; struct addrinfo hints, *res, *tres; struct address_cache *ad_cache; char *tmpaddr; /* VARIABLES PROTECTED BY rpcbaddr_cache_lock: ad_cache */ /* Get the address of the rpcbind. Check cache first */ client = NULL; addr_to_delete.len = 0; rwlock_rdlock(&rpcbaddr_cache_lock); ad_cache = NULL; if (host != NULL) ad_cache = check_cache(host, nconf->nc_netid); if (ad_cache != NULL) { addr = ad_cache->ac_taddr; client = clnt_tli_create(RPC_ANYFD, nconf, addr, (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0); if (client != NULL) { if (targaddr) *targaddr = strdup(ad_cache->ac_uaddr); rwlock_unlock(&rpcbaddr_cache_lock); return (client); } addr_to_delete.len = addr->len; addr_to_delete.buf = (char *)malloc(addr->len); if (addr_to_delete.buf == NULL) { addr_to_delete.len = 0; } else { memcpy(addr_to_delete.buf, addr->buf, addr->len); } } rwlock_unlock(&rpcbaddr_cache_lock); if (addr_to_delete.len != 0) { /* * Assume this may be due to cache data being * outdated */ rwlock_wrlock(&rpcbaddr_cache_lock); delete_cache(&addr_to_delete); rwlock_unlock(&rpcbaddr_cache_lock); free(addr_to_delete.buf); } if (!__rpc_nconf2sockinfo(nconf, &si)) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; return NULL; } memset(&hints, 0, sizeof hints); hints.ai_family = si.si_af; hints.ai_socktype = si.si_socktype; hints.ai_protocol = si.si_proto; #ifdef CLNT_DEBUG printf("trying netid %s family %d proto %d socktype %d\n", nconf->nc_netid, si.si_af, si.si_proto, si.si_socktype); #endif if (nconf->nc_protofmly != NULL && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) { client = local_rpcb(); if (! client) { #ifdef ND_DEBUG clnt_pcreateerror("rpcbind clnt interface"); #endif return (NULL); } else { struct sockaddr_un sun; if (targaddr) { *targaddr = malloc(sizeof(sun.sun_path)); if (*targaddr == NULL) { CLNT_DESTROY(client); return (NULL); } strncpy(*targaddr, _PATH_RPCBINDSOCK, sizeof(sun.sun_path)); } return (client); } } else { if (getaddrinfo(host, "sunrpc", &hints, &res) != 0) { rpc_createerr.cf_stat = RPC_UNKNOWNHOST; return NULL; } } for (tres = res; tres != NULL; tres = tres->ai_next) { taddr.buf = tres->ai_addr; taddr.len = taddr.maxlen = tres->ai_addrlen; #ifdef ND_DEBUG { char *ua; ua = taddr2uaddr(nconf, &taddr); fprintf(stderr, "Got it [%s]\n", ua); free(ua); } #endif #ifdef ND_DEBUG { int i; fprintf(stderr, "\tnetbuf len = %d, maxlen = %d\n", taddr.len, taddr.maxlen); fprintf(stderr, "\tAddress is "); for (i = 0; i < taddr.len; i++) fprintf(stderr, "%u.", ((char *)(taddr.buf))[i]); fprintf(stderr, "\n"); } #endif client = clnt_tli_create(RPC_ANYFD, nconf, &taddr, (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0); #ifdef ND_DEBUG if (! client) { clnt_pcreateerror("rpcbind clnt interface"); } #endif if (client) { tmpaddr = targaddr ? taddr2uaddr(nconf, &taddr) : NULL; add_cache(host, nconf->nc_netid, &taddr, tmpaddr); if (targaddr) *targaddr = tmpaddr; break; } } if (res) freeaddrinfo(res); return (client); } #endif /* XXX */ #define IN4_LOCALHOST_STRING "127.0.0.1" #define IN6_LOCALHOST_STRING "::1" /* * This routine will return a client handle that is connected to the local * rpcbind. Returns NULL on error and free's everything. */ static CLIENT * local_rpcb() { CLIENT *client; struct socket *so; size_t tsize; struct sockaddr_un sun; int error; /* * Try connecting to the local rpcbind through a local socket * first. If this doesn't work, try all transports defined in * the netconfig file. */ memset(&sun, 0, sizeof sun); so = NULL; error = socreate(AF_LOCAL, &so, SOCK_STREAM, 0, curthread->td_ucred, curthread); if (error) goto try_nconf; sun.sun_family = AF_LOCAL; strcpy(sun.sun_path, _PATH_RPCBINDSOCK); sun.sun_len = SUN_LEN(&sun); tsize = __rpc_get_t_size(AF_LOCAL, 0, 0); client = clnt_vc_create(so, (struct sockaddr *)&sun, (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS, tsize, tsize, 1); if (client != NULL) { /* Mark the socket to be closed in destructor */ (void) CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL); return client; } /* Nobody needs this socket anymore; free the descriptor. */ soclose(so); try_nconf: #if 0 static struct netconfig *loopnconf; static char *localhostname; /* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */ mutex_lock(&loopnconf_lock); if (loopnconf == NULL) { struct netconfig *nconf, *tmpnconf = NULL; void *nc_handle; int fd; nc_handle = setnetconfig(); if (nc_handle == NULL) { /* fails to open netconfig file */ syslog (LOG_ERR, "rpc: failed to open " NETCONFIG); rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; mutex_unlock(&loopnconf_lock); return (NULL); } while ((nconf = getnetconfig(nc_handle)) != NULL) { if (( #ifdef INET6 strcmp(nconf->nc_protofmly, NC_INET6) == 0 || #endif strcmp(nconf->nc_protofmly, NC_INET) == 0) && (nconf->nc_semantics == NC_TPI_COTS || nconf->nc_semantics == NC_TPI_COTS_ORD)) { fd = __rpc_nconf2fd(nconf); /* * Can't create a socket, assume that * this family isn't configured in the kernel. */ if (fd < 0) continue; _close(fd); tmpnconf = nconf; if (!strcmp(nconf->nc_protofmly, NC_INET)) localhostname = IN4_LOCALHOST_STRING; else localhostname = IN6_LOCALHOST_STRING; } } if (tmpnconf == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; mutex_unlock(&loopnconf_lock); return (NULL); } loopnconf = getnetconfigent(tmpnconf->nc_netid); /* loopnconf is never freed */ endnetconfig(nc_handle); } mutex_unlock(&loopnconf_lock); client = getclnthandle(localhostname, loopnconf, NULL); return (client); #else return (NULL); #endif } /* * Set a mapping between program, version and address. * Calls the rpcbind service to do the mapping. */ bool_t rpcb_set(rpcprog_t program, rpcvers_t version, const struct netconfig *nconf, /* Network structure of transport */ const struct netbuf *address) /* Services netconfig address */ { CLIENT *client; bool_t rslt = FALSE; RPCB parms; #if 0 char uidbuf[32]; #endif struct netconfig nconfcopy; struct netbuf addresscopy; /* parameter checking */ if (nconf == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; return (FALSE); } if (address == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNADDR; return (FALSE); } client = local_rpcb(); if (! client) { return (FALSE); } /* convert to universal */ /*LINTED const castaway*/ nconfcopy = *nconf; addresscopy = *address; parms.r_addr = taddr2uaddr(&nconfcopy, &addresscopy); if (!parms.r_addr) { CLNT_DESTROY(client); rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; return (FALSE); /* no universal address */ } parms.r_prog = program; parms.r_vers = version; parms.r_netid = nconf->nc_netid; #if 0 /* * Though uid is not being used directly, we still send it for * completeness. For non-unix platforms, perhaps some other * string or an empty string can be sent. */ (void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid()); parms.r_owner = uidbuf; #else parms.r_owner = ""; #endif CLNT_CALL(client, (rpcproc_t)RPCBPROC_SET, (xdrproc_t) xdr_rpcb, (char *)(void *)&parms, (xdrproc_t) xdr_bool, (char *)(void *)&rslt, tottimeout); CLNT_DESTROY(client); free(parms.r_addr, M_RPC); return (rslt); } /* * Remove the mapping between program, version and netbuf address. * Calls the rpcbind service to do the un-mapping. * If netbuf is NULL, unset for all the transports, otherwise unset * only for the given transport. */ bool_t rpcb_unset(rpcprog_t program, rpcvers_t version, const struct netconfig *nconf) { CLIENT *client; bool_t rslt = FALSE; RPCB parms; #if 0 char uidbuf[32]; #endif client = local_rpcb(); if (! client) { return (FALSE); } parms.r_prog = program; parms.r_vers = version; if (nconf) parms.r_netid = nconf->nc_netid; else { /*LINTED const castaway*/ parms.r_netid = (char *)(uintptr_t) &nullstring[0]; /* unsets all */ } /*LINTED const castaway*/ parms.r_addr = (char *)(uintptr_t) &nullstring[0]; #if 0 (void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid()); parms.r_owner = uidbuf; #else parms.r_owner = ""; #endif CLNT_CALL(client, (rpcproc_t)RPCBPROC_UNSET, (xdrproc_t) xdr_rpcb, (char *)(void *)&parms, (xdrproc_t) xdr_bool, (char *)(void *)&rslt, tottimeout); CLNT_DESTROY(client); return (rslt); } #if 0 /* * From the merged list, find the appropriate entry */ static struct netbuf * got_entry(relp, nconf) rpcb_entry_list_ptr relp; const struct netconfig *nconf; { struct netbuf *na = NULL; rpcb_entry_list_ptr sp; rpcb_entry *rmap; for (sp = relp; sp != NULL; sp = sp->rpcb_entry_next) { rmap = &sp->rpcb_entry_map; if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) && (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) && (nconf->nc_semantics == rmap->r_nc_semantics) && (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != 0)) { na = uaddr2taddr(nconf, rmap->r_maddr); #ifdef ND_DEBUG fprintf(stderr, "\tRemote address is [%s].\n", rmap->r_maddr); if (!na) fprintf(stderr, "\tCouldn't resolve remote address!\n"); #endif break; } } return (na); } /* * Quick check to see if rpcbind is up. Tries to connect over * local transport. */ static bool_t __rpcbind_is_up() { struct netconfig *nconf; struct sockaddr_un sun; void *localhandle; int sock; nconf = NULL; localhandle = setnetconfig(); while ((nconf = getnetconfig(localhandle)) != NULL) { if (nconf->nc_protofmly != NULL && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) break; } if (nconf == NULL) return (FALSE); endnetconfig(localhandle); memset(&sun, 0, sizeof sun); sock = _socket(AF_LOCAL, SOCK_STREAM, 0); if (sock < 0) return (FALSE); sun.sun_family = AF_LOCAL; strncpy(sun.sun_path, _PATH_RPCBINDSOCK, sizeof(sun.sun_path)); sun.sun_len = SUN_LEN(&sun); if (_connect(sock, (struct sockaddr *)&sun, sun.sun_len) < 0) { _close(sock); return (FALSE); } _close(sock); return (TRUE); } /* * An internal function which optimizes rpcb_getaddr function. It also * returns the client handle that it uses to contact the remote rpcbind. * * The algorithm used: If the transports is TCP or UDP, it first tries * version 2 (portmap), 4 and then 3 (svr4). This order should be * changed in the next OS release to 4, 2 and 3. We are assuming that by * that time, version 4 would be available on many machines on the network. * With this algorithm, we get performance as well as a plan for * obsoleting version 2. * * For all other transports, the algorithm remains as 4 and then 3. * * XXX: Due to some problems with t_connect(), we do not reuse the same client * handle for COTS cases and hence in these cases we do not return the * client handle. This code will change if t_connect() ever * starts working properly. Also look under clnt_vc.c. */ struct netbuf * __rpcb_findaddr_timed(program, version, nconf, host, clpp, tp) rpcprog_t program; rpcvers_t version; const struct netconfig *nconf; const char *host; CLIENT **clpp; struct timeval *tp; { static bool_t check_rpcbind = TRUE; CLIENT *client = NULL; RPCB parms; enum clnt_stat clnt_st; char *ua = NULL; rpcvers_t vers; struct netbuf *address = NULL; rpcvers_t start_vers = RPCBVERS4; struct netbuf servaddr; /* parameter checking */ if (nconf == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; return (NULL); } parms.r_addr = NULL; /* * Use default total timeout if no timeout is specified. */ if (tp == NULL) tp = &tottimeout; #ifdef PORTMAP /* Try version 2 for TCP or UDP */ if (strcmp(nconf->nc_protofmly, NC_INET) == 0) { u_short port = 0; struct netbuf remote; rpcvers_t pmapvers = 2; struct pmap pmapparms; /* * Try UDP only - there are some portmappers out * there that use UDP only. */ if (strcmp(nconf->nc_proto, NC_TCP) == 0) { struct netconfig *newnconf; if ((newnconf = getnetconfigent("udp")) == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; return (NULL); } client = getclnthandle(host, newnconf, &parms.r_addr); freenetconfigent(newnconf); } else { client = getclnthandle(host, nconf, &parms.r_addr); } if (client == NULL) return (NULL); /* * Set version and retry timeout. */ CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime); CLNT_CONTROL(client, CLSET_VERS, (char *)&pmapvers); pmapparms.pm_prog = program; pmapparms.pm_vers = version; pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ? IPPROTO_UDP : IPPROTO_TCP; pmapparms.pm_port = 0; /* not needed */ clnt_st = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT, (xdrproc_t) xdr_pmap, (caddr_t)(void *)&pmapparms, (xdrproc_t) xdr_u_short, (caddr_t)(void *)&port, *tp); if (clnt_st != RPC_SUCCESS) { if ((clnt_st == RPC_PROGVERSMISMATCH) || (clnt_st == RPC_PROGUNAVAIL)) goto try_rpcbind; /* Try different versions */ rpc_createerr.cf_stat = RPC_PMAPFAILURE; clnt_geterr(client, &rpc_createerr.cf_error); goto error; } else if (port == 0) { address = NULL; rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; goto error; } port = htons(port); CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)&remote); if (((address = (struct netbuf *) malloc(sizeof (struct netbuf))) == NULL) || ((address->buf = (char *) malloc(remote.len)) == NULL)) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; clnt_geterr(client, &rpc_createerr.cf_error); if (address) { free(address); address = NULL; } goto error; } memcpy(address->buf, remote.buf, remote.len); memcpy(&((char *)address->buf)[sizeof (short)], (char *)(void *)&port, sizeof (short)); address->len = address->maxlen = remote.len; goto done; } #endif /* PORTMAP */ try_rpcbind: /* * Check if rpcbind is up. This prevents needless delays when * accessing applications such as the keyserver while booting * disklessly. */ if (check_rpcbind && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) { if (!__rpcbind_is_up()) { rpc_createerr.cf_stat = RPC_PMAPFAILURE; rpc_createerr.cf_error.re_errno = 0; goto error; } check_rpcbind = FALSE; } /* * Now we try version 4 and then 3. * We also send the remote system the address we used to * contact it in case it can help to connect back with us */ parms.r_prog = program; parms.r_vers = version; /*LINTED const castaway*/ parms.r_owner = (char *) &nullstring[0]; /* not needed; */ /* just for xdring */ parms.r_netid = nconf->nc_netid; /* not really needed */ /* * If a COTS transport is being used, try getting address via CLTS * transport. This works only with version 4. */ if (nconf->nc_semantics == NC_TPI_COTS_ORD || nconf->nc_semantics == NC_TPI_COTS) { void *handle; struct netconfig *nconf_clts; rpcb_entry_list_ptr relp = NULL; if (client == NULL) { /* This did not go through the above PORTMAP/TCP code */ if ((handle = __rpc_setconf("datagram_v")) != NULL) { while ((nconf_clts = __rpc_getconf(handle)) != NULL) { if (strcmp(nconf_clts->nc_protofmly, nconf->nc_protofmly) != 0) { continue; } client = getclnthandle(host, nconf_clts, &parms.r_addr); break; } __rpc_endconf(handle); } if (client == NULL) goto regular_rpcbind; /* Go the regular way */ } else { /* This is a UDP PORTMAP handle. Change to version 4 */ vers = RPCBVERS4; CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers); } /* * We also send the remote system the address we used to * contact it in case it can help it connect back with us */ if (parms.r_addr == NULL) { /*LINTED const castaway*/ parms.r_addr = (char *) &nullstring[0]; /* for XDRing */ } CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime); clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDRLIST, (xdrproc_t) xdr_rpcb, (char *)(void *)&parms, (xdrproc_t) xdr_rpcb_entry_list_ptr, (char *)(void *)&relp, *tp); if (clnt_st == RPC_SUCCESS) { if ((address = got_entry(relp, nconf)) != NULL) { xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr, (char *)(void *)&relp); CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)(void *)&servaddr); __rpc_fixup_addr(address, &servaddr); goto done; } /* Entry not found for this transport */ xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr, (char *)(void *)&relp); /* * XXX: should have perhaps returned with error but * since the remote machine might not always be able * to send the address on all transports, we try the * regular way with regular_rpcbind */ goto regular_rpcbind; } else if ((clnt_st == RPC_PROGVERSMISMATCH) || (clnt_st == RPC_PROGUNAVAIL)) { start_vers = RPCBVERS; /* Try version 3 now */ goto regular_rpcbind; /* Try different versions */ } else { rpc_createerr.cf_stat = RPC_PMAPFAILURE; clnt_geterr(client, &rpc_createerr.cf_error); goto error; } } regular_rpcbind: /* Now the same transport is to be used to get the address */ if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) || (nconf->nc_semantics == NC_TPI_COTS))) { /* A CLTS type of client - destroy it */ CLNT_DESTROY(client); client = NULL; } if (client == NULL) { client = getclnthandle(host, nconf, &parms.r_addr); if (client == NULL) { goto error; } } if (parms.r_addr == NULL) { /*LINTED const castaway*/ parms.r_addr = (char *) &nullstring[0]; } /* First try from start_vers and then version 3 (RPCBVERS) */ CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *) &rpcbrmttime); for (vers = start_vers; vers >= RPCBVERS; vers--) { /* Set the version */ CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers); clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR, (xdrproc_t) xdr_rpcb, (char *)(void *)&parms, (xdrproc_t) xdr_wrapstring, (char *)(void *) &ua, *tp); if (clnt_st == RPC_SUCCESS) { if ((ua == NULL) || (ua[0] == 0)) { /* address unknown */ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; goto error; } address = uaddr2taddr(nconf, ua); #ifdef ND_DEBUG fprintf(stderr, "\tRemote address is [%s]\n", ua); if (!address) fprintf(stderr, "\tCouldn't resolve remote address!\n"); #endif xdr_free((xdrproc_t)xdr_wrapstring, (char *)(void *)&ua); if (! address) { /* We don't know about your universal address */ rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; goto error; } CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)(void *)&servaddr); __rpc_fixup_addr(address, &servaddr); goto done; } else if (clnt_st == RPC_PROGVERSMISMATCH) { struct rpc_err rpcerr; clnt_geterr(client, &rpcerr); if (rpcerr.re_vers.low > RPCBVERS4) goto error; /* a new version, can't handle */ } else if (clnt_st != RPC_PROGUNAVAIL) { /* Cant handle this error */ rpc_createerr.cf_stat = clnt_st; clnt_geterr(client, &rpc_createerr.cf_error); goto error; } } error: if (client) { CLNT_DESTROY(client); client = NULL; } done: if (nconf->nc_semantics != NC_TPI_CLTS) { /* This client is the connectionless one */ if (client) { CLNT_DESTROY(client); client = NULL; } } if (clpp) { *clpp = client; } else if (client) { CLNT_DESTROY(client); } if (parms.r_addr != NULL && parms.r_addr != nullstring) free(parms.r_addr); return (address); } /* * Find the mapped address for program, version. * Calls the rpcbind service remotely to do the lookup. * Uses the transport specified in nconf. * Returns FALSE (0) if no map exists, else returns 1. * * Assuming that the address is all properly allocated */ bool_t rpcb_getaddr(program, version, nconf, address, host) rpcprog_t program; rpcvers_t version; const struct netconfig *nconf; struct netbuf *address; const char *host; { struct netbuf *na; if ((na = __rpcb_findaddr_timed(program, version, (struct netconfig *) nconf, (char *) host, (CLIENT **) NULL, (struct timeval *) NULL)) == NULL) return (FALSE); if (na->len > address->maxlen) { /* Too long address */ free(na->buf); free(na); rpc_createerr.cf_stat = RPC_FAILED; return (FALSE); } memcpy(address->buf, na->buf, (size_t)na->len); address->len = na->len; free(na->buf); free(na); return (TRUE); } /* * Get a copy of the current maps. * Calls the rpcbind service remotely to get the maps. * * It returns only a list of the services * It returns NULL on failure. */ rpcblist * rpcb_getmaps(nconf, host) const struct netconfig *nconf; const char *host; { rpcblist_ptr head = NULL; CLIENT *client; enum clnt_stat clnt_st; rpcvers_t vers = 0; client = getclnthandle(host, nconf, NULL); if (client == NULL) { return (head); } clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr, (char *)(void *)&head, tottimeout); if (clnt_st == RPC_SUCCESS) goto done; if ((clnt_st != RPC_PROGVERSMISMATCH) && (clnt_st != RPC_PROGUNAVAIL)) { rpc_createerr.cf_stat = RPC_RPCBFAILURE; clnt_geterr(client, &rpc_createerr.cf_error); goto done; } /* fall back to earlier version */ CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers); if (vers == RPCBVERS4) { vers = RPCBVERS; CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers); if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr, (char *)(void *)&head, tottimeout) == RPC_SUCCESS) goto done; } rpc_createerr.cf_stat = RPC_RPCBFAILURE; clnt_geterr(client, &rpc_createerr.cf_error); done: CLNT_DESTROY(client); return (head); } /* * rpcbinder remote-call-service interface. * This routine is used to call the rpcbind remote call service * which will look up a service program in the address maps, and then * remotely call that routine with the given parameters. This allows * programs to do a lookup and call in one step. */ enum clnt_stat rpcb_rmtcall(nconf, host, prog, vers, proc, xdrargs, argsp, xdrres, resp, tout, addr_ptr) const struct netconfig *nconf; /* Netconfig structure */ const char *host; /* Remote host name */ rpcprog_t prog; rpcvers_t vers; rpcproc_t proc; /* Remote proc identifiers */ xdrproc_t xdrargs, xdrres; /* XDR routines */ caddr_t argsp, resp; /* Argument and Result */ struct timeval tout; /* Timeout value for this call */ const struct netbuf *addr_ptr; /* Preallocated netbuf address */ { CLIENT *client; enum clnt_stat stat; struct r_rpcb_rmtcallargs a; struct r_rpcb_rmtcallres r; rpcvers_t rpcb_vers; stat = 0; client = getclnthandle(host, nconf, NULL); if (client == NULL) { return (RPC_FAILED); } /*LINTED const castaway*/ CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)(void *)&rmttimeout); a.prog = prog; a.vers = vers; a.proc = proc; a.args.args_val = argsp; a.xdr_args = xdrargs; r.addr = NULL; r.results.results_val = resp; r.xdr_res = xdrres; for (rpcb_vers = RPCBVERS4; rpcb_vers >= RPCBVERS; rpcb_vers--) { CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&rpcb_vers); stat = CLNT_CALL(client, (rpcproc_t)RPCBPROC_CALLIT, (xdrproc_t) xdr_rpcb_rmtcallargs, (char *)(void *)&a, (xdrproc_t) xdr_rpcb_rmtcallres, (char *)(void *)&r, tout); if ((stat == RPC_SUCCESS) && (addr_ptr != NULL)) { struct netbuf *na; /*LINTED const castaway*/ na = uaddr2taddr((struct netconfig *) nconf, r.addr); if (!na) { stat = RPC_N2AXLATEFAILURE; /*LINTED const castaway*/ ((struct netbuf *) addr_ptr)->len = 0; goto error; } if (na->len > addr_ptr->maxlen) { /* Too long address */ stat = RPC_FAILED; /* XXX A better error no */ free(na->buf); free(na); /*LINTED const castaway*/ ((struct netbuf *) addr_ptr)->len = 0; goto error; } memcpy(addr_ptr->buf, na->buf, (size_t)na->len); /*LINTED const castaway*/ ((struct netbuf *)addr_ptr)->len = na->len; free(na->buf); free(na); break; } else if ((stat != RPC_PROGVERSMISMATCH) && (stat != RPC_PROGUNAVAIL)) { goto error; } } error: CLNT_DESTROY(client); if (r.addr) xdr_free((xdrproc_t) xdr_wrapstring, (char *)(void *)&r.addr); return (stat); } /* * Gets the time on the remote host. * Returns 1 if succeeds else 0. */ bool_t rpcb_gettime(host, timep) const char *host; time_t *timep; { CLIENT *client = NULL; void *handle; struct netconfig *nconf; rpcvers_t vers; enum clnt_stat st; if ((host == NULL) || (host[0] == 0)) { time(timep); return (TRUE); } if ((handle = __rpc_setconf("netpath")) == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; return (FALSE); } rpc_createerr.cf_stat = RPC_SUCCESS; while (client == NULL) { if ((nconf = __rpc_getconf(handle)) == NULL) { if (rpc_createerr.cf_stat == RPC_SUCCESS) rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; break; } client = getclnthandle(host, nconf, NULL); if (client) break; } __rpc_endconf(handle); if (client == (CLIENT *) NULL) { return (FALSE); } st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_int, (char *)(void *)timep, tottimeout); if ((st == RPC_PROGVERSMISMATCH) || (st == RPC_PROGUNAVAIL)) { CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers); if (vers == RPCBVERS4) { /* fall back to earlier version */ vers = RPCBVERS; CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers); st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_int, (char *)(void *)timep, tottimeout); } } CLNT_DESTROY(client); return (st == RPC_SUCCESS? TRUE: FALSE); } static bool_t xdr_netbuf(XDR *xdrs, struct netbuf *objp) { bool_t dummy; void **pp; if (!xdr_uint32_t(xdrs, (uint32_t *) &objp->maxlen)) { return (FALSE); } pp = &objp->buf; + + if (objp->maxlen > RPC_MAXDATASIZE) { + return (FALSE); + } + dummy = xdr_bytes(xdrs, (char **) pp, (u_int *)&(objp->len), objp->maxlen); return (dummy); } /* * Converts taddr to universal address. This routine should never * really be called because local n2a libraries are always provided. */ char * rpcb_taddr2uaddr(struct netconfig *nconf, struct netbuf *taddr) { CLIENT *client; char *uaddr = NULL; /* parameter checking */ if (nconf == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; return (NULL); } if (taddr == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNADDR; return (NULL); } client = local_rpcb(); if (! client) { return (NULL); } CLNT_CALL(client, (rpcproc_t)RPCBPROC_TADDR2UADDR, (xdrproc_t) xdr_netbuf, (char *)(void *)taddr, (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr, tottimeout); CLNT_DESTROY(client); return (uaddr); } /* * Converts universal address to netbuf. This routine should never * really be called because local n2a libraries are always provided. */ struct netbuf * rpcb_uaddr2taddr(struct netconfig *nconf, char *uaddr) { CLIENT *client; struct netbuf *taddr; /* parameter checking */ if (nconf == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; return (NULL); } if (uaddr == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNADDR; return (NULL); } client = local_rpcb(); if (! client) { return (NULL); } taddr = (struct netbuf *)malloc(sizeof (struct netbuf), M_RPC, M_WAITOK|M_ZERO); if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_UADDR2TADDR, (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr, (xdrproc_t) xdr_netbuf, (char *)(void *)taddr, tottimeout) != RPC_SUCCESS) { free(taddr); taddr = NULL; } CLNT_DESTROY(client); return (taddr); } #endif Index: head/sys/rpc/rpcb_prot.c =================================================================== --- head/sys/rpc/rpcb_prot.c (revision 319368) +++ head/sys/rpc/rpcb_prot.c (revision 319369) @@ -1,243 +1,244 @@ /* $NetBSD: rpcb_prot.c,v 1.3 2000/07/14 08:40:42 fvdl Exp $ */ /*- * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1986-1991 by Sun Microsystems Inc. */ /* #ident "@(#)rpcb_prot.c 1.13 94/04/24 SMI" */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rpcb_prot.c 1.9 89/04/21 Copyr 1984 Sun Micro"; #endif #include __FBSDID("$FreeBSD$"); /* * rpcb_prot.c * XDR routines for the rpcbinder version 3. * * Copyright (C) 1984, 1988, Sun Microsystems, Inc. */ #include #include #include #include #include +#include #include bool_t xdr_portmap(XDR *xdrs, struct portmap *regs) { if (xdr_u_long(xdrs, ®s->pm_prog) && xdr_u_long(xdrs, ®s->pm_vers) && xdr_u_long(xdrs, ®s->pm_prot)) return (xdr_u_long(xdrs, ®s->pm_port)); return (FALSE); } bool_t xdr_rpcb(XDR *xdrs, RPCB *objp) { if (!xdr_uint32_t(xdrs, &objp->r_prog)) { return (FALSE); } if (!xdr_uint32_t(xdrs, &objp->r_vers)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->r_netid, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_netid, RPC_MAXDATASIZE)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->r_addr, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_addr, RPC_MAXDATASIZE)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->r_owner, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_owner, RPC_MAXDATASIZE)) { return (FALSE); } return (TRUE); } /* * rpcblist_ptr implements a linked list. The RPCL definition from * rpcb_prot.x is: * * struct rpcblist { * rpcb rpcb_map; * struct rpcblist *rpcb_next; * }; * typedef rpcblist *rpcblist_ptr; * * Recall that "pointers" in XDR are encoded as a boolean, indicating whether * there's any data behind the pointer, followed by the data (if any exists). * The boolean can be interpreted as ``more data follows me''; if FALSE then * nothing follows the boolean; if TRUE then the boolean is followed by an * actual struct rpcb, and another rpcblist_ptr (declared in RPCL as "struct * rpcblist *"). * * This could be implemented via the xdr_pointer type, though this would * result in one recursive call per element in the list. Rather than do that * we can ``unwind'' the recursion into a while loop and use xdr_reference to * serialize the rpcb elements. */ bool_t xdr_rpcblist_ptr(XDR *xdrs, rpcblist_ptr *rp) { /* * more_elements is pre-computed in case the direction is * XDR_ENCODE or XDR_FREE. more_elements is overwritten by * xdr_bool when the direction is XDR_DECODE. */ bool_t more_elements; int freeing = (xdrs->x_op == XDR_FREE); rpcblist_ptr next; rpcblist_ptr next_copy; next = NULL; for (;;) { more_elements = (bool_t)(*rp != NULL); if (! xdr_bool(xdrs, &more_elements)) { return (FALSE); } if (! more_elements) { return (TRUE); /* we are done */ } /* * the unfortunate side effect of non-recursion is that in * the case of freeing we must remember the next object * before we free the current object ... */ if (freeing && *rp) next = (*rp)->rpcb_next; if (! xdr_reference(xdrs, (caddr_t *)rp, (u_int)sizeof (RPCBLIST), (xdrproc_t)xdr_rpcb)) { return (FALSE); } if (freeing) { next_copy = next; rp = &next_copy; /* * Note that in the subsequent iteration, next_copy * gets nulled out by the xdr_reference * but next itself survives. */ } else if (*rp) { rp = &((*rp)->rpcb_next); } } /*NOTREACHED*/ } #if 0 /* * xdr_rpcblist() is specified to take a RPCBLIST **, but is identical in * functionality to xdr_rpcblist_ptr(). */ bool_t xdr_rpcblist(XDR *xdrs, RPCBLIST **rp) { bool_t dummy; dummy = xdr_rpcblist_ptr(xdrs, (rpcblist_ptr *)rp); return (dummy); } #endif bool_t xdr_rpcb_entry(XDR *xdrs, rpcb_entry *objp) { - if (!xdr_string(xdrs, &objp->r_maddr, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_maddr, RPC_MAXDATASIZE)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->r_nc_netid, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_nc_netid, RPC_MAXDATASIZE)) { return (FALSE); } if (!xdr_uint32_t(xdrs, &objp->r_nc_semantics)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->r_nc_protofmly, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_nc_protofmly, RPC_MAXDATASIZE)) { return (FALSE); } - if (!xdr_string(xdrs, &objp->r_nc_proto, (u_int)~0)) { + if (!xdr_string(xdrs, &objp->r_nc_proto, RPC_MAXDATASIZE)) { return (FALSE); } return (TRUE); } bool_t xdr_rpcb_entry_list_ptr(XDR *xdrs, rpcb_entry_list_ptr *rp) { /* * more_elements is pre-computed in case the direction is * XDR_ENCODE or XDR_FREE. more_elements is overwritten by * xdr_bool when the direction is XDR_DECODE. */ bool_t more_elements; int freeing = (xdrs->x_op == XDR_FREE); rpcb_entry_list_ptr next; rpcb_entry_list_ptr next_copy; next = NULL; for (;;) { more_elements = (bool_t)(*rp != NULL); if (! xdr_bool(xdrs, &more_elements)) { return (FALSE); } if (! more_elements) { return (TRUE); /* we are done */ } /* * the unfortunate side effect of non-recursion is that in * the case of freeing we must remember the next object * before we free the current object ... */ if (freeing) next = (*rp)->rpcb_entry_next; if (! xdr_reference(xdrs, (caddr_t *)rp, (u_int)sizeof (rpcb_entry_list), (xdrproc_t)xdr_rpcb_entry)) { return (FALSE); } if (freeing && *rp) { next_copy = next; rp = &next_copy; /* * Note that in the subsequent iteration, next_copy * gets nulled out by the xdr_reference * but next itself survives. */ } else if (*rp) { rp = &((*rp)->rpcb_entry_next); } } /*NOTREACHED*/ } Index: head/sys/xdr/xdr.c =================================================================== --- head/sys/xdr/xdr.c (revision 319368) +++ head/sys/xdr/xdr.c (revision 319369) @@ -1,816 +1,836 @@ /* $NetBSD: xdr.c,v 1.22 2000/07/06 03:10:35 christos Exp $ */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ #if defined(LIBC_SCCS) && !defined(lint) static char *sccsid2 = "@(#)xdr.c 1.35 87/08/12"; static char *sccsid = "@(#)xdr.c 2.1 88/07/29 4.0 RPCSRC"; #endif #include __FBSDID("$FreeBSD$"); /* * xdr.c, Generic XDR routines implementation. * * Copyright (C) 1986, Sun Microsystems, Inc. * * These are the "generic" xdr routines used to serialize and de-serialize * most common data items. See xdr.h for more info on the interface to * xdr. */ #include #include #include #include +#include +#include #include #include typedef quad_t longlong_t; /* ANSI long long type */ typedef u_quad_t u_longlong_t; /* ANSI unsigned long long type */ /* * constants specific to the xdr "protocol" */ #define XDR_FALSE ((long) 0) #define XDR_TRUE ((long) 1) -#define LASTUNSIGNED ((u_int) 0-1) /* * for unit alignment */ static const char xdr_zero[BYTES_PER_XDR_UNIT] = { 0, 0, 0, 0 }; /* * Free a data structure using XDR * Not a filter, but a convenient utility nonetheless */ void xdr_free(xdrproc_t proc, void *objp) { XDR x; x.x_op = XDR_FREE; (*proc)(&x, objp); } /* * XDR nothing */ bool_t xdr_void(void) { return (TRUE); } /* * XDR integers */ bool_t xdr_int(XDR *xdrs, int *ip) { long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (long) *ip; return (XDR_PUTLONG(xdrs, &l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, &l)) { return (FALSE); } *ip = (int) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned integers */ bool_t xdr_u_int(XDR *xdrs, u_int *up) { u_long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (u_long) *up; return (XDR_PUTLONG(xdrs, (long *)&l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, (long *)&l)) { return (FALSE); } *up = (u_int) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR long integers * same as xdr_u_long - open coded to save a proc call! */ bool_t xdr_long(XDR *xdrs, long *lp) { switch (xdrs->x_op) { case XDR_ENCODE: return (XDR_PUTLONG(xdrs, lp)); case XDR_DECODE: return (XDR_GETLONG(xdrs, lp)); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned long integers * same as xdr_long - open coded to save a proc call! */ bool_t xdr_u_long(XDR *xdrs, u_long *ulp) { switch (xdrs->x_op) { case XDR_ENCODE: return (XDR_PUTLONG(xdrs, (long *)ulp)); case XDR_DECODE: return (XDR_GETLONG(xdrs, (long *)ulp)); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR 32-bit integers * same as xdr_uint32_t - open coded to save a proc call! */ bool_t xdr_int32_t(XDR *xdrs, int32_t *int32_p) { long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (long) *int32_p; return (XDR_PUTLONG(xdrs, &l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, &l)) { return (FALSE); } *int32_p = (int32_t) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned 32-bit integers * same as xdr_int32_t - open coded to save a proc call! */ bool_t xdr_uint32_t(XDR *xdrs, uint32_t *uint32_p) { u_long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (u_long) *uint32_p; return (XDR_PUTLONG(xdrs, (long *)&l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, (long *)&l)) { return (FALSE); } *uint32_p = (uint32_t) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR short integers */ bool_t xdr_short(XDR *xdrs, short *sp) { long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (long) *sp; return (XDR_PUTLONG(xdrs, &l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, &l)) { return (FALSE); } *sp = (short) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned short integers */ bool_t xdr_u_short(XDR *xdrs, u_short *usp) { u_long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (u_long) *usp; return (XDR_PUTLONG(xdrs, (long *)&l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, (long *)&l)) { return (FALSE); } *usp = (u_short) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR 16-bit integers */ bool_t xdr_int16_t(XDR *xdrs, int16_t *int16_p) { long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (long) *int16_p; return (XDR_PUTLONG(xdrs, &l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, &l)) { return (FALSE); } *int16_p = (int16_t) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned 16-bit integers */ bool_t xdr_uint16_t(XDR *xdrs, uint16_t *uint16_p) { u_long l; switch (xdrs->x_op) { case XDR_ENCODE: l = (u_long) *uint16_p; return (XDR_PUTLONG(xdrs, (long *)&l)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, (long *)&l)) { return (FALSE); } *uint16_p = (uint16_t) l; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR a char */ bool_t xdr_char(XDR *xdrs, char *cp) { int i; i = (*cp); if (!xdr_int(xdrs, &i)) { return (FALSE); } *cp = i; return (TRUE); } /* * XDR an unsigned char */ bool_t xdr_u_char(XDR *xdrs, u_char *cp) { u_int u; u = (*cp); if (!xdr_u_int(xdrs, &u)) { return (FALSE); } *cp = u; return (TRUE); } /* * XDR booleans */ bool_t xdr_bool(XDR *xdrs, bool_t *bp) { long lb; switch (xdrs->x_op) { case XDR_ENCODE: lb = *bp ? XDR_TRUE : XDR_FALSE; return (XDR_PUTLONG(xdrs, &lb)); case XDR_DECODE: if (!XDR_GETLONG(xdrs, &lb)) { return (FALSE); } *bp = (lb == XDR_FALSE) ? FALSE : TRUE; return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR enumerations */ bool_t xdr_enum(XDR *xdrs, enum_t *ep) { enum sizecheck { SIZEVAL }; /* used to find the size of an enum */ /* * enums are treated as ints */ /* LINTED */ if (sizeof (enum sizecheck) == sizeof (long)) { return (xdr_long(xdrs, (long *)(void *)ep)); } else /* LINTED */ if (sizeof (enum sizecheck) == sizeof (int)) { return (xdr_int(xdrs, (int *)(void *)ep)); } else /* LINTED */ if (sizeof (enum sizecheck) == sizeof (short)) { return (xdr_short(xdrs, (short *)(void *)ep)); } else { return (FALSE); } } /* * XDR opaque data * Allows the specification of a fixed size sequence of opaque bytes. * cp points to the opaque object and cnt gives the byte length. */ bool_t xdr_opaque(XDR *xdrs, caddr_t cp, u_int cnt) { u_int rndup; static int crud[BYTES_PER_XDR_UNIT]; /* * if no data we are done */ if (cnt == 0) return (TRUE); /* * round byte count to full xdr units */ rndup = cnt % BYTES_PER_XDR_UNIT; if (rndup > 0) rndup = BYTES_PER_XDR_UNIT - rndup; if (xdrs->x_op == XDR_DECODE) { if (!XDR_GETBYTES(xdrs, cp, cnt)) { return (FALSE); } if (rndup == 0) return (TRUE); return (XDR_GETBYTES(xdrs, (caddr_t)(void *)crud, rndup)); } if (xdrs->x_op == XDR_ENCODE) { if (!XDR_PUTBYTES(xdrs, cp, cnt)) { return (FALSE); } if (rndup == 0) return (TRUE); return (XDR_PUTBYTES(xdrs, xdr_zero, rndup)); } if (xdrs->x_op == XDR_FREE) { return (TRUE); } return (FALSE); } /* * XDR counted bytes * *cpp is a pointer to the bytes, *sizep is the count. * If *cpp is NULL maxsize bytes are allocated */ bool_t xdr_bytes(XDR *xdrs, char **cpp, u_int *sizep, u_int maxsize) { char *sp = *cpp; /* sp is the actual string pointer */ u_int nodesize; + bool_t ret, allocated = FALSE; /* * first deal with the length since xdr bytes are counted */ if (! xdr_u_int(xdrs, sizep)) { return (FALSE); } nodesize = *sizep; if ((nodesize > maxsize) && (xdrs->x_op != XDR_FREE)) { return (FALSE); } /* * now deal with the actual bytes */ switch (xdrs->x_op) { case XDR_DECODE: if (nodesize == 0) { return (TRUE); } if (sp == NULL) { *cpp = sp = mem_alloc(nodesize); + allocated = TRUE; } if (sp == NULL) { printf("xdr_bytes: out of memory"); return (FALSE); } /* FALLTHROUGH */ case XDR_ENCODE: - return (xdr_opaque(xdrs, sp, nodesize)); + ret = xdr_opaque(xdrs, sp, nodesize); + if ((xdrs->x_op == XDR_DECODE) && (ret == FALSE)) { + if (allocated == TRUE) { + mem_free(sp, nodesize); + *cpp = NULL; + } + } + return (ret); case XDR_FREE: if (sp != NULL) { mem_free(sp, nodesize); *cpp = NULL; } return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * Implemented here due to commonality of the object. */ bool_t xdr_netobj(XDR *xdrs, struct netobj *np) { return (xdr_bytes(xdrs, &np->n_bytes, &np->n_len, MAX_NETOBJ_SZ)); } /* * XDR a descriminated union * Support routine for discriminated unions. * You create an array of xdrdiscrim structures, terminated with * an entry with a null procedure pointer. The routine gets * the discriminant value and then searches the array of xdrdiscrims * looking for that value. It calls the procedure given in the xdrdiscrim * to handle the discriminant. If there is no specific routine a default * routine may be called. * If there is no specific or default routine an error is returned. */ bool_t xdr_union(XDR *xdrs, enum_t *dscmp, /* enum to decide which arm to work on */ char *unp, /* the union itself */ const struct xdr_discrim *choices, /* [value, xdr proc] for each arm */ xdrproc_t dfault) /* default xdr routine */ { enum_t dscm; /* * we deal with the discriminator; it's an enum */ if (! xdr_enum(xdrs, dscmp)) { return (FALSE); } dscm = *dscmp; /* * search choices for a value that matches the discriminator. * if we find one, execute the xdr routine for that value. */ for (; choices->proc != NULL_xdrproc_t; choices++) { if (choices->value == dscm) return ((*(choices->proc))(xdrs, unp)); } /* * no match - execute the default xdr routine if there is one */ return ((dfault == NULL_xdrproc_t) ? FALSE : (*dfault)(xdrs, unp)); } /* * Non-portable xdr primitives. * Care should be taken when moving these routines to new architectures. */ /* * XDR null terminated ASCII strings * xdr_string deals with "C strings" - arrays of bytes that are * terminated by a NULL character. The parameter cpp references a * pointer to storage; If the pointer is null, then the necessary * storage is allocated. The last parameter is the max allowed length * of the string as specified by a protocol. */ bool_t xdr_string(XDR *xdrs, char **cpp, u_int maxsize) { char *sp = *cpp; /* sp is the actual string pointer */ u_int size; u_int nodesize; + bool_t ret, allocated = FALSE; /* * first deal with the length since xdr strings are counted-strings */ switch (xdrs->x_op) { case XDR_FREE: if (sp == NULL) { return(TRUE); /* already free */ } /* FALLTHROUGH */ case XDR_ENCODE: size = strlen(sp); break; case XDR_DECODE: break; } if (! xdr_u_int(xdrs, &size)) { return (FALSE); } if (size > maxsize) { return (FALSE); } nodesize = size + 1; /* * now deal with the actual bytes */ switch (xdrs->x_op) { case XDR_DECODE: if (nodesize == 0) { return (TRUE); } - if (sp == NULL) + if (sp == NULL) { *cpp = sp = mem_alloc(nodesize); + allocated = TRUE; + } if (sp == NULL) { printf("xdr_string: out of memory"); return (FALSE); } sp[size] = 0; /* FALLTHROUGH */ case XDR_ENCODE: - return (xdr_opaque(xdrs, sp, size)); + ret = xdr_opaque(xdrs, sp, size); + if ((xdrs->x_op == XDR_DECODE) && (ret == FALSE)) { + if (allocated == TRUE) { + mem_free(sp, nodesize); + *cpp = NULL; + } + } + return (ret); case XDR_FREE: mem_free(sp, nodesize); *cpp = NULL; return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * Wrapper for xdr_string that can be called directly from * routines like clnt_call */ bool_t xdr_wrapstring(XDR *xdrs, char **cpp) { - return xdr_string(xdrs, cpp, LASTUNSIGNED); + return xdr_string(xdrs, cpp, RPC_MAXDATASIZE); } /* * NOTE: xdr_hyper(), xdr_u_hyper(), xdr_longlong_t(), and xdr_u_longlong_t() * are in the "non-portable" section because they require that a `long long' * be a 64-bit type. * * --thorpej@netbsd.org, November 30, 1999 */ /* * XDR 64-bit integers */ bool_t xdr_int64_t(XDR *xdrs, int64_t *llp) { u_long ul[2]; switch (xdrs->x_op) { case XDR_ENCODE: ul[0] = (u_long)((uint64_t)*llp >> 32) & 0xffffffff; ul[1] = (u_long)((uint64_t)*llp) & 0xffffffff; if (XDR_PUTLONG(xdrs, (long *)&ul[0]) == FALSE) return (FALSE); return (XDR_PUTLONG(xdrs, (long *)&ul[1])); case XDR_DECODE: if (XDR_GETLONG(xdrs, (long *)&ul[0]) == FALSE) return (FALSE); if (XDR_GETLONG(xdrs, (long *)&ul[1]) == FALSE) return (FALSE); *llp = (int64_t) (((uint64_t)ul[0] << 32) | ((uint64_t)ul[1])); return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR unsigned 64-bit integers */ bool_t xdr_uint64_t(XDR *xdrs, uint64_t *ullp) { u_long ul[2]; switch (xdrs->x_op) { case XDR_ENCODE: ul[0] = (u_long)(*ullp >> 32) & 0xffffffff; ul[1] = (u_long)(*ullp) & 0xffffffff; if (XDR_PUTLONG(xdrs, (long *)&ul[0]) == FALSE) return (FALSE); return (XDR_PUTLONG(xdrs, (long *)&ul[1])); case XDR_DECODE: if (XDR_GETLONG(xdrs, (long *)&ul[0]) == FALSE) return (FALSE); if (XDR_GETLONG(xdrs, (long *)&ul[1]) == FALSE) return (FALSE); *ullp = (uint64_t) (((uint64_t)ul[0] << 32) | ((uint64_t)ul[1])); return (TRUE); case XDR_FREE: return (TRUE); } /* NOTREACHED */ return (FALSE); } /* * XDR hypers */ bool_t xdr_hyper(XDR *xdrs, longlong_t *llp) { /* * Don't bother open-coding this; it's a fair amount of code. Just * call xdr_int64_t(). */ return (xdr_int64_t(xdrs, (int64_t *)llp)); } /* * XDR unsigned hypers */ bool_t xdr_u_hyper(XDR *xdrs, u_longlong_t *ullp) { /* * Don't bother open-coding this; it's a fair amount of code. Just * call xdr_uint64_t(). */ return (xdr_uint64_t(xdrs, (uint64_t *)ullp)); } /* * XDR longlong_t's */ bool_t xdr_longlong_t(XDR *xdrs, longlong_t *llp) { /* * Don't bother open-coding this; it's a fair amount of code. Just * call xdr_int64_t(). */ return (xdr_int64_t(xdrs, (int64_t *)llp)); } /* * XDR u_longlong_t's */ bool_t xdr_u_longlong_t(XDR *xdrs, u_longlong_t *ullp) { /* * Don't bother open-coding this; it's a fair amount of code. Just * call xdr_uint64_t(). */ return (xdr_uint64_t(xdrs, (uint64_t *)ullp)); } Index: head/usr.sbin/rpcbind/rpcb_svc_com.c =================================================================== --- head/usr.sbin/rpcbind/rpcb_svc_com.c (revision 319368) +++ head/usr.sbin/rpcbind/rpcb_svc_com.c (revision 319369) @@ -1,1486 +1,1488 @@ /* $NetBSD: rpcb_svc_com.c,v 1.9 2002/11/08 00:16:39 fvdl Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. */ /* #ident "@(#)rpcb_svc_com.c 1.18 94/05/02 SMI" */ /* * rpcb_svc_com.c * The commom server procedure for the rpcbind. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PORTMAP #include +#include #include #endif /* PORTMAP */ #include #include #include "rpcbind.h" #define RPC_BUF_MAX 65536 /* can be raised if required */ static char *nullstring = ""; static int rpcb_rmtcalls; struct rmtcallfd_list { int fd; SVCXPRT *xprt; char *netid; struct rmtcallfd_list *next; }; #define NFORWARD 64 #define MAXTIME_OFF 300 /* 5 minutes */ struct finfo { int flag; #define FINFO_ACTIVE 0x1 u_int32_t caller_xid; struct netbuf *caller_addr; u_int32_t forward_xid; int forward_fd; char *uaddr; rpcproc_t reply_type; rpcvers_t versnum; time_t time; }; static struct finfo FINFO[NFORWARD]; static bool_t xdr_encap_parms(XDR *, struct encap_parms *); static bool_t xdr_rmtcall_args(XDR *, struct r_rmtcall_args *); static bool_t xdr_rmtcall_result(XDR *, struct r_rmtcall_args *); static bool_t xdr_opaque_parms(XDR *, struct r_rmtcall_args *); static int find_rmtcallfd_by_netid(char *); static SVCXPRT *find_rmtcallxprt_by_fd(int); static int forward_register(u_int32_t, struct netbuf *, int, char *, rpcproc_t, rpcvers_t, u_int32_t *); static struct finfo *forward_find(u_int32_t); static int free_slot_by_xid(u_int32_t); static int free_slot_by_index(int); static int netbufcmp(struct netbuf *, struct netbuf *); static struct netbuf *netbufdup(struct netbuf *); static void netbuffree(struct netbuf *); static int check_rmtcalls(struct pollfd *, int); static void xprt_set_caller(SVCXPRT *, struct finfo *); static void send_svcsyserr(SVCXPRT *, struct finfo *); static void handle_reply(int, SVCXPRT *); static void find_versions(rpcprog_t, char *, rpcvers_t *, rpcvers_t *); static rpcblist_ptr find_service(rpcprog_t, rpcvers_t, char *); static char *getowner(SVCXPRT *, char *, size_t); static int add_pmaplist(RPCB *); static int del_pmaplist(RPCB *); /* * Set a mapping of program, version, netid */ /* ARGSUSED */ void * rpcbproc_set_com(void *arg, struct svc_req *rqstp __unused, SVCXPRT *transp, rpcvers_t rpcbversnum) { RPCB *regp = (RPCB *)arg; static bool_t ans; char owner[64]; #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, "RPCB_SET request for (%lu, %lu, %s, %s) : ", (unsigned long)regp->r_prog, (unsigned long)regp->r_vers, regp->r_netid, regp->r_addr); #endif ans = map_set(regp, getowner(transp, owner, sizeof owner)); #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed"); #endif /* XXX: should have used some defined constant here */ rpcbs_set(rpcbversnum - 2, ans); return (void *)&ans; } bool_t map_set(RPCB *regp, char *owner) { RPCB reg, *a; rpcblist_ptr rbl, fnd; reg = *regp; /* * check to see if already used * find_service returns a hit even if * the versions don't match, so check for it */ fnd = find_service(reg.r_prog, reg.r_vers, reg.r_netid); if (fnd && (fnd->rpcb_map.r_vers == reg.r_vers)) { if (!strcmp(fnd->rpcb_map.r_addr, reg.r_addr)) /* * if these match then it is already * registered so just say "OK". */ return (TRUE); else return (FALSE); } /* * add to the end of the list */ rbl = malloc(sizeof (RPCBLIST)); if (rbl == NULL) return (FALSE); a = &(rbl->rpcb_map); a->r_prog = reg.r_prog; a->r_vers = reg.r_vers; a->r_netid = strdup(reg.r_netid); a->r_addr = strdup(reg.r_addr); a->r_owner = strdup(owner); if (!a->r_addr || !a->r_netid || !a->r_owner) { if (a->r_netid) free(a->r_netid); if (a->r_addr) free(a->r_addr); if (a->r_owner) free(a->r_owner); free(rbl); return (FALSE); } rbl->rpcb_next = (rpcblist_ptr)NULL; if (list_rbl == NULL) { list_rbl = rbl; } else { for (fnd = list_rbl; fnd->rpcb_next; fnd = fnd->rpcb_next) ; fnd->rpcb_next = rbl; } #ifdef PORTMAP (void) add_pmaplist(regp); #endif return (TRUE); } /* * Unset a mapping of program, version, netid */ /* ARGSUSED */ void * rpcbproc_unset_com(void *arg, struct svc_req *rqstp __unused, SVCXPRT *transp, rpcvers_t rpcbversnum) { RPCB *regp = (RPCB *)arg; static bool_t ans; char owner[64]; #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, "RPCB_UNSET request for (%lu, %lu, %s) : ", (unsigned long)regp->r_prog, (unsigned long)regp->r_vers, regp->r_netid); #endif ans = map_unset(regp, getowner(transp, owner, sizeof owner)); #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed"); #endif /* XXX: should have used some defined constant here */ rpcbs_unset(rpcbversnum - 2, ans); return (void *)&ans; } bool_t map_unset(RPCB *regp, char *owner) { int ans = 0; rpcblist_ptr rbl, prev, tmp; if (owner == NULL) return (0); for (prev = NULL, rbl = list_rbl; rbl; /* cstyle */) { if ((rbl->rpcb_map.r_prog != regp->r_prog) || (rbl->rpcb_map.r_vers != regp->r_vers) || (regp->r_netid[0] && strcasecmp(regp->r_netid, rbl->rpcb_map.r_netid))) { /* both rbl & prev move forwards */ prev = rbl; rbl = rbl->rpcb_next; continue; } /* * Check whether appropriate uid. Unset only * if superuser or the owner itself. */ if (strcmp(owner, "superuser") && strcmp(rbl->rpcb_map.r_owner, owner)) return (0); /* found it; rbl moves forward, prev stays */ ans = 1; tmp = rbl; rbl = rbl->rpcb_next; if (prev == NULL) list_rbl = rbl; else prev->rpcb_next = rbl; free(tmp->rpcb_map.r_addr); free(tmp->rpcb_map.r_netid); free(tmp->rpcb_map.r_owner); free(tmp); } #ifdef PORTMAP if (ans) (void) del_pmaplist(regp); #endif /* * We return 1 either when the entry was not there or it * was able to unset it. It can come to this point only if * atleast one of the conditions is true. */ return (1); } void delete_prog(unsigned int prog) { RPCB reg; register rpcblist_ptr rbl; for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { if ((rbl->rpcb_map.r_prog != prog)) continue; if (is_bound(rbl->rpcb_map.r_netid, rbl->rpcb_map.r_addr)) continue; reg.r_prog = rbl->rpcb_map.r_prog; reg.r_vers = rbl->rpcb_map.r_vers; reg.r_netid = strdup(rbl->rpcb_map.r_netid); (void) map_unset(®, "superuser"); free(reg.r_netid); } } void * rpcbproc_getaddr_com(RPCB *regp, struct svc_req *rqstp __unused, SVCXPRT *transp, rpcvers_t rpcbversnum, rpcvers_t verstype) { static char *uaddr; char *saddr = NULL; rpcblist_ptr fnd; if (uaddr != NULL && uaddr != nullstring) { free(uaddr); uaddr = NULL; } fnd = find_service(regp->r_prog, regp->r_vers, transp->xp_netid); if (fnd && ((verstype == RPCB_ALLVERS) || (regp->r_vers == fnd->rpcb_map.r_vers))) { if (*(regp->r_addr) != '\0') { /* may contain a hint about */ saddr = regp->r_addr; /* the interface that we */ } /* should use */ if (!(uaddr = mergeaddr(transp, transp->xp_netid, fnd->rpcb_map.r_addr, saddr))) { /* Try whatever we have */ uaddr = strdup(fnd->rpcb_map.r_addr); } else if (!uaddr[0]) { /* * The server died. Unset all versions of this prog. */ delete_prog(regp->r_prog); uaddr = nullstring; } } else { uaddr = nullstring; } #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, "getaddr: %s\n", uaddr); #endif /* XXX: should have used some defined constant here */ rpcbs_getaddr(rpcbversnum - 2, regp->r_prog, regp->r_vers, transp->xp_netid, uaddr); return (void *)&uaddr; } /* ARGSUSED */ void * rpcbproc_gettime_com(void *arg __unused, struct svc_req *rqstp __unused, SVCXPRT *transp __unused, rpcvers_t rpcbversnum __unused) { static time_t curtime; (void) time(&curtime); return (void *)&curtime; } /* * Convert uaddr to taddr. Should be used only by * local servers/clients. (kernel level stuff only) */ /* ARGSUSED */ void * rpcbproc_uaddr2taddr_com(void *arg, struct svc_req *rqstp __unused, SVCXPRT *transp, rpcvers_t rpcbversnum __unused) { char **uaddrp = (char **)arg; struct netconfig *nconf; static struct netbuf nbuf; static struct netbuf *taddr; if (taddr) { free(taddr->buf); free(taddr); taddr = NULL; } if (((nconf = rpcbind_get_conf(transp->xp_netid)) == NULL) || ((taddr = uaddr2taddr(nconf, *uaddrp)) == NULL)) { (void) memset((char *)&nbuf, 0, sizeof (struct netbuf)); return (void *)&nbuf; } return (void *)taddr; } /* * Convert taddr to uaddr. Should be used only by * local servers/clients. (kernel level stuff only) */ /* ARGSUSED */ void * rpcbproc_taddr2uaddr_com(void *arg, struct svc_req *rqstp __unused, SVCXPRT *transp, rpcvers_t rpcbversnum __unused) { struct netbuf *taddr = (struct netbuf *)arg; static char *uaddr; struct netconfig *nconf; #ifdef CHEW_FDS int fd; if ((fd = open("/dev/null", O_RDONLY)) == -1) { uaddr = (char *)strerror(errno); return (&uaddr); } #endif /* CHEW_FDS */ if (uaddr != NULL && uaddr != nullstring) { free(uaddr); uaddr = NULL; } if (((nconf = rpcbind_get_conf(transp->xp_netid)) == NULL) || ((uaddr = taddr2uaddr(nconf, taddr)) == NULL)) { uaddr = nullstring; } return (void *)&uaddr; } static bool_t xdr_encap_parms(XDR *xdrs, struct encap_parms *epp) { - return (xdr_bytes(xdrs, &(epp->args), (u_int *) &(epp->arglen), ~0)); + return (xdr_bytes(xdrs, &(epp->args), (u_int *) &(epp->arglen), + RPC_MAXDATASIZE)); } /* * XDR remote call arguments. It ignores the address part. * written for XDR_DECODE direction only */ static bool_t xdr_rmtcall_args(XDR *xdrs, struct r_rmtcall_args *cap) { /* does not get the address or the arguments */ if (xdr_rpcprog(xdrs, &(cap->rmt_prog)) && xdr_rpcvers(xdrs, &(cap->rmt_vers)) && xdr_rpcproc(xdrs, &(cap->rmt_proc))) { return (xdr_encap_parms(xdrs, &(cap->rmt_args))); } return (FALSE); } /* * XDR remote call results along with the address. Ignore * program number, version number and proc number. * Written for XDR_ENCODE direction only. */ static bool_t xdr_rmtcall_result(XDR *xdrs, struct r_rmtcall_args *cap) { bool_t result; #ifdef PORTMAP if (cap->rmt_localvers == PMAPVERS) { int h1, h2, h3, h4, p1, p2; u_long port; /* interpret the universal address for TCP/IP */ if (sscanf(cap->rmt_uaddr, "%d.%d.%d.%d.%d.%d", &h1, &h2, &h3, &h4, &p1, &p2) != 6) return (FALSE); port = ((p1 & 0xff) << 8) + (p2 & 0xff); result = xdr_u_long(xdrs, &port); } else #endif if ((cap->rmt_localvers == RPCBVERS) || (cap->rmt_localvers == RPCBVERS4)) { result = xdr_wrapstring(xdrs, &(cap->rmt_uaddr)); } else { return (FALSE); } if (result == TRUE) return (xdr_encap_parms(xdrs, &(cap->rmt_args))); return (FALSE); } /* * only worries about the struct encap_parms part of struct r_rmtcall_args. * The arglen must already be set!! */ static bool_t xdr_opaque_parms(XDR *xdrs, struct r_rmtcall_args *cap) { return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen)); } static struct rmtcallfd_list *rmthead; static struct rmtcallfd_list *rmttail; int create_rmtcall_fd(struct netconfig *nconf) { int fd; struct rmtcallfd_list *rmt; SVCXPRT *xprt; if ((fd = __rpc_nconf2fd(nconf)) == -1) { if (debugging) fprintf(stderr, "create_rmtcall_fd: couldn't open \"%s\" (errno %d)\n", nconf->nc_device, errno); return (-1); } xprt = svc_tli_create(fd, 0, (struct t_bind *) 0, 0, 0); if (xprt == NULL) { if (debugging) fprintf(stderr, "create_rmtcall_fd: svc_tli_create failed\n"); return (-1); } rmt = malloc(sizeof (struct rmtcallfd_list)); if (rmt == NULL) { syslog(LOG_ERR, "create_rmtcall_fd: no memory!"); return (-1); } rmt->xprt = xprt; rmt->netid = strdup(nconf->nc_netid); xprt->xp_netid = rmt->netid; rmt->fd = fd; rmt->next = NULL; if (rmthead == NULL) { rmthead = rmt; rmttail = rmt; } else { rmttail->next = rmt; rmttail = rmt; } /* XXX not threadsafe */ if (fd > svc_maxfd) svc_maxfd = fd; FD_SET(fd, &svc_fdset); return (fd); } static int find_rmtcallfd_by_netid(char *netid) { struct rmtcallfd_list *rmt; for (rmt = rmthead; rmt != NULL; rmt = rmt->next) { if (strcmp(netid, rmt->netid) == 0) { return (rmt->fd); } } return (-1); } static SVCXPRT * find_rmtcallxprt_by_fd(int fd) { struct rmtcallfd_list *rmt; for (rmt = rmthead; rmt != NULL; rmt = rmt->next) { if (fd == rmt->fd) { return (rmt->xprt); } } return (NULL); } /* * Call a remote procedure service. This procedure is very quiet when things * go wrong. The proc is written to support broadcast rpc. In the broadcast * case, a machine should shut-up instead of complain, lest the requestor be * overrun with complaints at the expense of not hearing a valid reply. * When receiving a request and verifying that the service exists, we * * receive the request * * open a new TLI endpoint on the same transport on which we received * the original request * * remember the original request's XID (which requires knowing the format * of the svc_dg_data structure) * * forward the request, with a new XID, to the requested service, * remembering the XID used to send this request (for later use in * reassociating the answer with the original request), the requestor's * address, the file descriptor on which the forwarded request is * made and the service's address. * * mark the file descriptor on which we anticipate receiving a reply from * the service and one to select for in our private svc_run procedure * * At some time in the future, a reply will be received from the service to * which we forwarded the request. At that time, we detect that the socket * used was for forwarding (by looking through the finfo structures to see * whether the fd corresponds to one of those) and call handle_reply() to * * receive the reply * * bundle the reply, along with the service's universal address * * create a SVCXPRT structure and use a version of svc_sendreply * that allows us to specify the reply XID and destination, send the reply * to the original requestor. */ void rpcbproc_callit_com(struct svc_req *rqstp, SVCXPRT *transp, rpcproc_t reply_type, rpcvers_t versnum) { register rpcblist_ptr rbl; struct netconfig *nconf; struct netbuf *caller; struct r_rmtcall_args a; char *buf_alloc = NULL, *outbufp; char *outbuf_alloc = NULL; char buf[RPC_BUF_MAX], outbuf[RPC_BUF_MAX]; struct netbuf *na = (struct netbuf *) NULL; struct rpc_msg call_msg; int outlen; u_int sendsz; XDR outxdr; AUTH *auth; int fd = -1; char *uaddr, *m_uaddr = NULL, *local_uaddr = NULL; u_int32_t *xidp; struct __rpc_sockinfo si; struct sockaddr *localsa; struct netbuf tbuf; if (!__rpc_fd2sockinfo(transp->xp_fd, &si)) { if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); return; } if (si.si_socktype != SOCK_DGRAM) return; /* Only datagram type accepted */ sendsz = __rpc_get_t_size(si.si_af, si.si_proto, UDPMSGSIZE); if (sendsz == 0) { /* data transfer not supported */ if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); return; } /* * Should be multiple of 4 for XDR. */ sendsz = roundup(sendsz, 4); if (sendsz > RPC_BUF_MAX) { #ifdef notyet buf_alloc = alloca(sendsz); /* not in IDR2? */ #else buf_alloc = malloc(sendsz); #endif /* notyet */ if (buf_alloc == NULL) { if (debugging) fprintf(stderr, "rpcbproc_callit_com: No Memory!\n"); if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); return; } a.rmt_args.args = buf_alloc; } else { a.rmt_args.args = buf; } call_msg.rm_xid = 0; /* For error checking purposes */ if (!svc_getargs(transp, (xdrproc_t) xdr_rmtcall_args, (char *) &a)) { if (reply_type == RPCBPROC_INDIRECT) svcerr_decode(transp); if (debugging) fprintf(stderr, "rpcbproc_callit_com: svc_getargs failed\n"); goto error; } if (!check_callit(transp, &a, versnum)) { svcerr_weakauth(transp); goto error; } caller = svc_getrpccaller(transp); #ifdef RPCBIND_DEBUG if (debugging) { uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), caller); fprintf(stderr, "%s %s req for (%lu, %lu, %lu, %s) from %s : ", versnum == PMAPVERS ? "pmap_rmtcall" : versnum == RPCBVERS ? "rpcb_rmtcall" : versnum == RPCBVERS4 ? "rpcb_indirect" : "unknown", reply_type == RPCBPROC_INDIRECT ? "indirect" : "callit", (unsigned long)a.rmt_prog, (unsigned long)a.rmt_vers, (unsigned long)a.rmt_proc, transp->xp_netid, uaddr ? uaddr : "unknown"); if (uaddr) free(uaddr); } #endif rbl = find_service(a.rmt_prog, a.rmt_vers, transp->xp_netid); rpcbs_rmtcall(versnum - 2, reply_type, a.rmt_prog, a.rmt_vers, a.rmt_proc, transp->xp_netid, rbl); if (rbl == (rpcblist_ptr)NULL) { #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, "not found\n"); #endif if (reply_type == RPCBPROC_INDIRECT) svcerr_noprog(transp); goto error; } if (rbl->rpcb_map.r_vers != a.rmt_vers) { if (reply_type == RPCBPROC_INDIRECT) { rpcvers_t vers_low, vers_high; find_versions(a.rmt_prog, transp->xp_netid, &vers_low, &vers_high); svcerr_progvers(transp, vers_low, vers_high); } goto error; } #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, "found at uaddr %s\n", rbl->rpcb_map.r_addr); #endif /* * Check whether this entry is valid and a server is present * Mergeaddr() returns NULL if no such entry is present, and * returns "" if the entry was present but the server is not * present (i.e., it crashed). */ if (reply_type == RPCBPROC_INDIRECT) { uaddr = mergeaddr(transp, transp->xp_netid, rbl->rpcb_map.r_addr, NULL); if (uaddr == NULL || uaddr[0] == '\0') { svcerr_noprog(transp); if (uaddr != NULL) free(uaddr); goto error; } free(uaddr); } nconf = rpcbind_get_conf(transp->xp_netid); if (nconf == (struct netconfig *)NULL) { if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); if (debugging) fprintf(stderr, "rpcbproc_callit_com: rpcbind_get_conf failed\n"); goto error; } localsa = local_sa(((struct sockaddr *)caller->buf)->sa_family); if (localsa == NULL) { if (debugging) fprintf(stderr, "rpcbproc_callit_com: no local address\n"); goto error; } tbuf.len = tbuf.maxlen = localsa->sa_len; tbuf.buf = localsa; local_uaddr = addrmerge(&tbuf, rbl->rpcb_map.r_addr, NULL, nconf->nc_netid); m_uaddr = addrmerge(caller, rbl->rpcb_map.r_addr, NULL, nconf->nc_netid); #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, "merged uaddr %s\n", m_uaddr); #endif if ((fd = find_rmtcallfd_by_netid(nconf->nc_netid)) == -1) { if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); goto error; } xidp = __rpcb_get_dg_xidp(transp); switch (forward_register(*xidp, caller, fd, m_uaddr, reply_type, versnum, &call_msg.rm_xid)) { case 1: /* Success; forward_register() will free m_uaddr for us. */ m_uaddr = NULL; break; case 0: /* * A duplicate request for the slow server. Let's not * beat on it any more. */ if (debugging) fprintf(stderr, "rpcbproc_callit_com: duplicate request\n"); goto error; case -1: /* forward_register failed. Perhaps no memory. */ if (debugging) fprintf(stderr, "rpcbproc_callit_com: forward_register failed\n"); goto error; } #ifdef DEBUG_RMTCALL if (debugging) fprintf(stderr, "rpcbproc_callit_com: original XID %x, new XID %x\n", *xidp, call_msg.rm_xid); #endif call_msg.rm_direction = CALL; call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; call_msg.rm_call.cb_prog = a.rmt_prog; call_msg.rm_call.cb_vers = a.rmt_vers; if (sendsz > RPC_BUF_MAX) { #ifdef notyet outbuf_alloc = alloca(sendsz); /* not in IDR2? */ #else outbuf_alloc = malloc(sendsz); #endif /* notyet */ if (outbuf_alloc == NULL) { if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); if (debugging) fprintf(stderr, "rpcbproc_callit_com: No memory!\n"); goto error; } xdrmem_create(&outxdr, outbuf_alloc, sendsz, XDR_ENCODE); } else { xdrmem_create(&outxdr, outbuf, sendsz, XDR_ENCODE); } if (!xdr_callhdr(&outxdr, &call_msg)) { if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); if (debugging) fprintf(stderr, "rpcbproc_callit_com: xdr_callhdr failed\n"); goto error; } if (!xdr_u_int32_t(&outxdr, &(a.rmt_proc))) { if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); if (debugging) fprintf(stderr, "rpcbproc_callit_com: xdr_u_long failed\n"); goto error; } if (rqstp->rq_cred.oa_flavor == AUTH_NULL) { auth = authnone_create(); } else if (rqstp->rq_cred.oa_flavor == AUTH_SYS) { struct authunix_parms *au; au = (struct authunix_parms *)rqstp->rq_clntcred; auth = authunix_create(au->aup_machname, au->aup_uid, au->aup_gid, au->aup_len, au->aup_gids); if (auth == NULL) /* fall back */ auth = authnone_create(); } else { /* we do not support any other authentication scheme */ if (debugging) fprintf(stderr, "rpcbproc_callit_com: oa_flavor != AUTH_NONE and oa_flavor != AUTH_SYS\n"); if (reply_type == RPCBPROC_INDIRECT) svcerr_weakauth(transp); /* XXX too strong.. */ goto error; } if (auth == NULL) { if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); if (debugging) fprintf(stderr, "rpcbproc_callit_com: authwhatever_create returned NULL\n"); goto error; } if (!AUTH_MARSHALL(auth, &outxdr)) { if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); AUTH_DESTROY(auth); if (debugging) fprintf(stderr, "rpcbproc_callit_com: AUTH_MARSHALL failed\n"); goto error; } AUTH_DESTROY(auth); if (!xdr_opaque_parms(&outxdr, &a)) { if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); if (debugging) fprintf(stderr, "rpcbproc_callit_com: xdr_opaque_parms failed\n"); goto error; } outlen = (int) XDR_GETPOS(&outxdr); if (outbuf_alloc) outbufp = outbuf_alloc; else outbufp = outbuf; na = uaddr2taddr(nconf, local_uaddr); if (!na) { if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); goto error; } if (sendto(fd, outbufp, outlen, 0, (struct sockaddr *)na->buf, na->len) != outlen) { if (debugging) fprintf(stderr, "rpcbproc_callit_com: sendto failed: errno %d\n", errno); if (reply_type == RPCBPROC_INDIRECT) svcerr_systemerr(transp); goto error; } goto out; error: if (call_msg.rm_xid != 0) (void) free_slot_by_xid(call_msg.rm_xid); out: if (local_uaddr) free(local_uaddr); if (buf_alloc) free(buf_alloc); if (outbuf_alloc) free(outbuf_alloc); if (na) { free(na->buf); free(na); } if (m_uaddr != NULL) free(m_uaddr); } /* * Makes an entry into the FIFO for the given request. * Returns 1 on success, 0 if this is a duplicate request, or -1 on error. * *callxidp is set to the xid of the call. */ static int forward_register(u_int32_t caller_xid, struct netbuf *caller_addr, int forward_fd, char *uaddr, rpcproc_t reply_type, rpcvers_t versnum, u_int32_t *callxidp) { int i; int j = 0; time_t min_time, time_now; static u_int32_t lastxid; int entry = -1; min_time = FINFO[0].time; time_now = time((time_t *)0); /* initialization */ if (lastxid == 0) lastxid = time_now * NFORWARD; /* * Check if it is a duplicate entry. Then, * try to find an empty slot. If not available, then * use the slot with the earliest time. */ for (i = 0; i < NFORWARD; i++) { if (FINFO[i].flag & FINFO_ACTIVE) { if ((FINFO[i].caller_xid == caller_xid) && (FINFO[i].reply_type == reply_type) && (FINFO[i].versnum == versnum) && (!netbufcmp(FINFO[i].caller_addr, caller_addr))) { FINFO[i].time = time((time_t *)0); return (0); /* Duplicate entry */ } else { /* Should we wait any longer */ if ((time_now - FINFO[i].time) > MAXTIME_OFF) (void) free_slot_by_index(i); } } if (entry == -1) { if ((FINFO[i].flag & FINFO_ACTIVE) == 0) { entry = i; } else if (FINFO[i].time < min_time) { j = i; min_time = FINFO[i].time; } } } if (entry != -1) { /* use this empty slot */ j = entry; } else { (void) free_slot_by_index(j); } if ((FINFO[j].caller_addr = netbufdup(caller_addr)) == NULL) { return (-1); } rpcb_rmtcalls++; /* no of pending calls */ FINFO[j].flag = FINFO_ACTIVE; FINFO[j].reply_type = reply_type; FINFO[j].versnum = versnum; FINFO[j].time = time_now; FINFO[j].caller_xid = caller_xid; FINFO[j].forward_fd = forward_fd; /* * Though uaddr is not allocated here, it will still be freed * from free_slot_*(). */ FINFO[j].uaddr = uaddr; lastxid = lastxid + NFORWARD; /* Don't allow a zero xid below. */ if ((u_int32_t)(lastxid + NFORWARD) <= NFORWARD) lastxid = NFORWARD; FINFO[j].forward_xid = lastxid + j; /* encode slot */ *callxidp = FINFO[j].forward_xid; /* forward on this xid */ return (1); } static struct finfo * forward_find(u_int32_t reply_xid) { int i; i = reply_xid % (u_int32_t)NFORWARD; if ((FINFO[i].flag & FINFO_ACTIVE) && (FINFO[i].forward_xid == reply_xid)) { return (&FINFO[i]); } return (NULL); } static int free_slot_by_xid(u_int32_t xid) { int entry; entry = xid % (u_int32_t)NFORWARD; return (free_slot_by_index(entry)); } static int free_slot_by_index(int index) { struct finfo *fi; fi = &FINFO[index]; if (fi->flag & FINFO_ACTIVE) { netbuffree(fi->caller_addr); /* XXX may be too big, but can't access xprt array here */ if (fi->forward_fd >= svc_maxfd) svc_maxfd--; free(fi->uaddr); fi->flag &= ~FINFO_ACTIVE; rpcb_rmtcalls--; return (1); } return (0); } static int netbufcmp(struct netbuf *n1, struct netbuf *n2) { return ((n1->len != n2->len) || memcmp(n1->buf, n2->buf, n1->len)); } static bool_t netbuf_copybuf(struct netbuf *dst, const struct netbuf *src) { assert(src->len <= src->maxlen); if (dst->maxlen < src->len || dst->buf == NULL) { if (dst->buf != NULL) free(dst->buf); if ((dst->buf = calloc(1, src->maxlen)) == NULL) return (FALSE); dst->maxlen = src->maxlen; } dst->len = src->len; memcpy(dst->buf, src->buf, src->len); return (TRUE); } static struct netbuf * netbufdup(struct netbuf *ap) { struct netbuf *np; if ((np = calloc(1, sizeof(struct netbuf))) == NULL) return (NULL); if (netbuf_copybuf(np, ap) == FALSE) { free(np); return (NULL); } return (np); } static void netbuffree(struct netbuf *ap) { free(ap->buf); ap->buf = NULL; free(ap); } #define MASKVAL (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND) extern bool_t __svc_clean_idle(fd_set *, int, bool_t); void my_svc_run(void) { size_t nfds; struct pollfd pollfds[FD_SETSIZE]; int poll_ret, check_ret; int n; #ifdef SVC_RUN_DEBUG int i; #endif register struct pollfd *p; fd_set cleanfds; for (;;) { p = pollfds; for (n = 0; n <= svc_maxfd; n++) { if (FD_ISSET(n, &svc_fdset)) { p->fd = n; p->events = MASKVAL; p++; } } nfds = p - pollfds; poll_ret = 0; #ifdef SVC_RUN_DEBUG if (debugging) { fprintf(stderr, "polling for read on fd < "); for (i = 0, p = pollfds; i < nfds; i++, p++) if (p->events) fprintf(stderr, "%d ", p->fd); fprintf(stderr, ">\n"); } #endif switch (poll_ret = poll(pollfds, nfds, 30 * 1000)) { case -1: /* * We ignore all errors, continuing with the assumption * that it was set by the signal handlers (or any * other outside event) and not caused by poll(). */ case 0: cleanfds = svc_fdset; __svc_clean_idle(&cleanfds, 30, FALSE); continue; default: #ifdef SVC_RUN_DEBUG if (debugging) { fprintf(stderr, "poll returned read fds < "); for (i = 0, p = pollfds; i < nfds; i++, p++) if (p->revents) fprintf(stderr, "%d ", p->fd); fprintf(stderr, ">\n"); } #endif /* * If we found as many replies on callback fds * as the number of descriptors selectable which * poll() returned, there can be no more so we * don't call svc_getreq_poll. Otherwise, there * must be another so we must call svc_getreq_poll. */ if ((check_ret = check_rmtcalls(pollfds, nfds)) == poll_ret) continue; svc_getreq_poll(pollfds, poll_ret-check_ret); } #ifdef SVC_RUN_DEBUG if (debugging) { fprintf(stderr, "svc_maxfd now %u\n", svc_maxfd); } #endif } } static int check_rmtcalls(struct pollfd *pfds, int nfds) { int j, ncallbacks_found = 0, rmtcalls_pending; SVCXPRT *xprt; if (rpcb_rmtcalls == 0) return (0); rmtcalls_pending = rpcb_rmtcalls; for (j = 0; j < nfds; j++) { if ((xprt = find_rmtcallxprt_by_fd(pfds[j].fd)) != NULL) { if (pfds[j].revents) { ncallbacks_found++; #ifdef DEBUG_RMTCALL if (debugging) fprintf(stderr, "my_svc_run: polled on forwarding fd %d, netid %s - calling handle_reply\n", pfds[j].fd, xprt->xp_netid); #endif handle_reply(pfds[j].fd, xprt); pfds[j].revents = 0; if (ncallbacks_found >= rmtcalls_pending) { break; } } } } return (ncallbacks_found); } static void xprt_set_caller(SVCXPRT *xprt, struct finfo *fi) { u_int32_t *xidp; netbuf_copybuf(svc_getrpccaller(xprt), fi->caller_addr); xidp = __rpcb_get_dg_xidp(xprt); *xidp = fi->caller_xid; } /* * Call svcerr_systemerr() only if RPCBVERS4 */ static void send_svcsyserr(SVCXPRT *xprt, struct finfo *fi) { if (fi->reply_type == RPCBPROC_INDIRECT) { xprt_set_caller(xprt, fi); svcerr_systemerr(xprt); } return; } static void handle_reply(int fd, SVCXPRT *xprt) { XDR reply_xdrs; struct rpc_msg reply_msg; struct rpc_err reply_error; char *buffer; struct finfo *fi; int inlen, pos, len; struct r_rmtcall_args a; struct sockaddr_storage ss; socklen_t fromlen; #ifdef SVC_RUN_DEBUG char *uaddr; #endif buffer = malloc(RPC_BUF_MAX); if (buffer == NULL) goto done; do { fromlen = sizeof(ss); inlen = recvfrom(fd, buffer, RPC_BUF_MAX, 0, (struct sockaddr *)&ss, &fromlen); } while (inlen < 0 && errno == EINTR); if (inlen < 0) { if (debugging) fprintf(stderr, "handle_reply: recvfrom returned %d, errno %d\n", inlen, errno); goto done; } reply_msg.acpted_rply.ar_verf = _null_auth; reply_msg.acpted_rply.ar_results.where = 0; reply_msg.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void; xdrmem_create(&reply_xdrs, buffer, (u_int)inlen, XDR_DECODE); if (!xdr_replymsg(&reply_xdrs, &reply_msg)) { if (debugging) (void) fprintf(stderr, "handle_reply: xdr_replymsg failed\n"); goto done; } fi = forward_find(reply_msg.rm_xid); #ifdef SVC_RUN_DEBUG if (debugging) { fprintf(stderr, "handle_reply: reply xid: %d fi addr: %p\n", reply_msg.rm_xid, fi); } #endif if (fi == NULL) { goto done; } _seterr_reply(&reply_msg, &reply_error); if (reply_error.re_status != RPC_SUCCESS) { if (debugging) (void) fprintf(stderr, "handle_reply: %s\n", clnt_sperrno(reply_error.re_status)); send_svcsyserr(xprt, fi); goto done; } pos = XDR_GETPOS(&reply_xdrs); len = inlen - pos; a.rmt_args.args = &buffer[pos]; a.rmt_args.arglen = len; a.rmt_uaddr = fi->uaddr; a.rmt_localvers = fi->versnum; xprt_set_caller(xprt, fi); #ifdef SVC_RUN_DEBUG uaddr = taddr2uaddr(rpcbind_get_conf("udp"), svc_getrpccaller(xprt)); if (debugging) { fprintf(stderr, "handle_reply: forwarding address %s to %s\n", a.rmt_uaddr, uaddr ? uaddr : "unknown"); } if (uaddr) free(uaddr); #endif svc_sendreply(xprt, (xdrproc_t) xdr_rmtcall_result, (char *) &a); done: if (buffer) free(buffer); if (reply_msg.rm_xid == 0) { #ifdef SVC_RUN_DEBUG if (debugging) { fprintf(stderr, "handle_reply: NULL xid on exit!\n"); } #endif } else (void) free_slot_by_xid(reply_msg.rm_xid); return; } static void find_versions(rpcprog_t prog, char *netid, rpcvers_t *lowvp, rpcvers_t *highvp) { register rpcblist_ptr rbl; unsigned int lowv = 0; unsigned int highv = 0; for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { if ((rbl->rpcb_map.r_prog != prog) || ((rbl->rpcb_map.r_netid != NULL) && (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0))) continue; if (lowv == 0) { highv = rbl->rpcb_map.r_vers; lowv = highv; } else if (rbl->rpcb_map.r_vers < lowv) { lowv = rbl->rpcb_map.r_vers; } else if (rbl->rpcb_map.r_vers > highv) { highv = rbl->rpcb_map.r_vers; } } *lowvp = lowv; *highvp = highv; return; } /* * returns the item with the given program, version number and netid. * If that version number is not found, it returns the item with that * program number, so that address is now returned to the caller. The * caller when makes a call to this program, version number, the call * will fail and it will return with PROGVERS_MISMATCH. The user can * then determine the highest and the lowest version number for this * program using clnt_geterr() and use those program version numbers. * * Returns the RPCBLIST for the given prog, vers and netid */ static rpcblist_ptr find_service(rpcprog_t prog, rpcvers_t vers, char *netid) { register rpcblist_ptr hit = NULL; register rpcblist_ptr rbl; for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { if ((rbl->rpcb_map.r_prog != prog) || ((rbl->rpcb_map.r_netid != NULL) && (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0))) continue; hit = rbl; if (rbl->rpcb_map.r_vers == vers) break; } return (hit); } /* * Copies the name associated with the uid of the caller and returns * a pointer to it. Similar to getwd(). */ static char * getowner(SVCXPRT *transp, char *owner, size_t ownersize) { uid_t uid; if (__rpc_get_local_uid(transp, &uid) < 0) strlcpy(owner, "unknown", ownersize); else if (uid == 0) strlcpy(owner, "superuser", ownersize); else snprintf(owner, ownersize, "%d", uid); return owner; } #ifdef PORTMAP /* * Add this to the pmap list only if it is UDP or TCP. */ static int add_pmaplist(RPCB *arg) { struct pmap pmap; struct pmaplist *pml; int h1, h2, h3, h4, p1, p2; if (strcmp(arg->r_netid, udptrans) == 0) { /* It is UDP! */ pmap.pm_prot = IPPROTO_UDP; } else if (strcmp(arg->r_netid, tcptrans) == 0) { /* It is TCP */ pmap.pm_prot = IPPROTO_TCP; } else /* Not an IP protocol */ return (0); /* interpret the universal address for TCP/IP */ if (sscanf(arg->r_addr, "%d.%d.%d.%d.%d.%d", &h1, &h2, &h3, &h4, &p1, &p2) != 6) return (0); pmap.pm_port = ((p1 & 0xff) << 8) + (p2 & 0xff); pmap.pm_prog = arg->r_prog; pmap.pm_vers = arg->r_vers; /* * add to END of list */ pml = malloc(sizeof (struct pmaplist)); if (pml == NULL) { (void) syslog(LOG_ERR, "rpcbind: no memory!\n"); return (1); } pml->pml_map = pmap; pml->pml_next = NULL; if (list_pml == NULL) { list_pml = pml; } else { struct pmaplist *fnd; /* Attach to the end of the list */ for (fnd = list_pml; fnd->pml_next; fnd = fnd->pml_next) ; fnd->pml_next = pml; } return (0); } /* * Delete this from the pmap list only if it is UDP or TCP. */ static int del_pmaplist(RPCB *arg) { struct pmaplist *pml; struct pmaplist *prevpml, *fnd; unsigned long prot; if (strcmp(arg->r_netid, udptrans) == 0) { /* It is UDP! */ prot = IPPROTO_UDP; } else if (strcmp(arg->r_netid, tcptrans) == 0) { /* It is TCP */ prot = IPPROTO_TCP; } else if (arg->r_netid[0] == 0) { prot = 0; /* Remove all occurrences */ } else { /* Not an IP protocol */ return (0); } for (prevpml = NULL, pml = list_pml; pml; /* cstyle */) { if ((pml->pml_map.pm_prog != arg->r_prog) || (pml->pml_map.pm_vers != arg->r_vers) || (prot && (pml->pml_map.pm_prot != prot))) { /* both pml & prevpml move forwards */ prevpml = pml; pml = pml->pml_next; continue; } /* found it; pml moves forward, prevpml stays */ fnd = pml; pml = pml->pml_next; if (prevpml == NULL) list_pml = pml; else prevpml->pml_next = pml; free(fnd); } return (0); } #endif /* PORTMAP */