X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/55e303ae13a4cf49d70f2294092726f2fffb9ef2..ea3f04195ba4a5034c9c8e9b726d4f7ce96f1832:/bsd/nfs/krpc_subr.c diff --git a/bsd/nfs/krpc_subr.c b/bsd/nfs/krpc_subr.c index 8999fd218..345762a0b 100644 --- a/bsd/nfs/krpc_subr.c +++ b/bsd/nfs/krpc_subr.c @@ -1,17 +1,20 @@ /* - * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2016 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -19,12 +22,12 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ /* - * Copyright (c) 1994 Gordon Ross, Adam Glass + * Copyright (c) 1994 Gordon Ross, Adam Glass * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * @@ -62,12 +65,15 @@ * */ +#include +#if CONFIG_NFS_CLIENT + #include #include #include #include #include -#include +#include #include #include #include @@ -84,7 +90,7 @@ * Kernel support for Sun RPC * * Used currently for bootstrapping in nfs diskless configurations. - * + * * Note: will not work on variable-sized rpc args/results. * implicit size-limit of an mbuf. */ @@ -94,35 +100,36 @@ */ struct auth_info { - u_int32_t rp_atype; /* auth type */ - u_int32_t rp_alen; /* auth length */ + u_int32_t rp_atype; /* auth type */ + u_int32_t rp_alen; /* auth length */ }; struct rpc_call { - u_int32_t rp_xid; /* request transaction id */ - int32_t rp_direction; /* call direction (0) */ - u_int32_t rp_rpcvers; /* rpc version (2) */ - u_int32_t rp_prog; /* program */ - u_int32_t rp_vers; /* version */ - u_int32_t rp_proc; /* procedure */ - struct auth_info rp_auth; - struct auth_info rp_verf; + u_int32_t rp_xid; /* request transaction id */ + int32_t rp_direction; /* call direction (0) */ + u_int32_t rp_rpcvers; /* rpc version (2) */ + u_int32_t rp_prog; /* program */ + u_int32_t rp_vers; /* version */ + u_int32_t rp_proc; /* procedure */ + struct auth_info rp_auth; + struct auth_info rp_verf; }; struct rpc_reply { - u_int32_t rp_xid; /* request transaction id */ - int32_t rp_direction; /* call direction (1) */ - int32_t rp_astatus; /* accept status (0: accepted) */ + u_int32_t rp_xid; /* request transaction id */ + int32_t rp_direction; /* call direction (1) */ + int32_t rp_astatus; /* accept status (0: accepted) */ union { u_int32_t rpu_errno; struct { struct auth_info rp_auth; - u_int32_t rp_rstatus; + u_int32_t rp_rstatus; } rpu_ok; } rp_u; }; -#define MIN_REPLY_HDR 16 /* xid, dir, astat, errno */ +#define MIN_REPLY_HDR 16 /* xid, dir, astat, errno */ +#define REPLY_SIZE 24 /* xid, dir, astat, rpu_ok */ /* * What is the longest we will wait before re-sending a request? @@ -130,7 +137,7 @@ struct rpc_reply { * The re-send loop count sup linearly to this maximum, so the * first complaint will happen after (1+2+3+4+5)=15 seconds. */ -#define MAX_RESEND_DELAY 5 /* seconds */ +#define MAX_RESEND_DELAY 5 /* seconds */ /* copied over from nfs_boot.c for printf format. could put in .h file... */ #define IP_FORMAT "%d.%d.%d.%d" @@ -143,22 +150,22 @@ struct rpc_reply { * Returns non-zero error on failure. */ int -krpc_portmap(sin, prog, vers, portp) - struct sockaddr_in *sin; /* server address */ - u_int prog, vers; /* host order */ - u_int16_t *portp; /* network order */ +krpc_portmap( + struct sockaddr_in *sin, /* server address */ + u_int prog, u_int vers, u_int proto, /* host order */ + u_int16_t *portp) /* network order */ { struct sdata { - u_int32_t prog; /* call program */ - u_int32_t vers; /* call version */ - u_int32_t proto; /* call protocol */ - u_int32_t port; /* call port (unused) */ + u_int32_t prog; /* call program */ + u_int32_t vers; /* call version */ + u_int32_t proto; /* call protocol */ + u_int32_t port; /* call port (unused) */ } *sdata; struct rdata { u_int16_t pad; u_int16_t port; } *rdata; - struct mbuf *m; + mbuf_t m; int error; /* The portmapper port is fixed. */ @@ -167,30 +174,38 @@ krpc_portmap(sin, prog, vers, portp) return 0; } - m = m_gethdr(M_WAIT, MT_DATA); - if (m == NULL) - return ENOBUFS; - m->m_len = sizeof(*sdata); - m->m_pkthdr.len = m->m_len; - sdata = mtod(m, struct sdata *); + error = mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &m); + if (error) { + return error; + } + mbuf_setlen(m, sizeof(*sdata)); + mbuf_pkthdr_setlen(m, sizeof(*sdata)); + sdata = mbuf_data(m); /* Do the RPC to get it. */ sdata->prog = htonl(prog); sdata->vers = htonl(vers); - sdata->proto = htonl(IPPROTO_UDP); + sdata->proto = htonl(proto); sdata->port = 0; sin->sin_port = htons(PMAPPORT); - error = krpc_call(sin, PMAPPROG, PMAPVERS, - PMAPPROC_GETPORT, &m, NULL); - if (error) + error = krpc_call(sin, SOCK_DGRAM, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT, &m, NULL); + if (error) { return error; + } + + rdata = mbuf_data(m); + + if (mbuf_len(m) >= sizeof(*rdata)) { + *portp = rdata->port; + } - rdata = mtod(m, struct rdata *); - *portp = rdata->port; + if (mbuf_len(m) < sizeof(*rdata) || !rdata->port) { + error = EPROGUNAVAIL; + } - m_freem(m); - return 0; + mbuf_freem(m); + return error; } /* @@ -199,69 +214,61 @@ krpc_portmap(sin, prog, vers, portp) * the address from whence the response came is saved there. */ int -krpc_call(sa, prog, vers, func, data, from_p) - struct sockaddr_in *sa; - u_int prog, vers, func; - struct mbuf **data; /* input/output */ - struct sockaddr_in **from_p; /* output */ +krpc_call( + struct sockaddr_in *sa, + u_int sotype, u_int prog, u_int vers, u_int func, + mbuf_t *data, /* input/output */ + struct sockaddr_in *from_p) /* output */ { - struct socket *so; + socket_t so; struct sockaddr_in *sin; - struct mbuf *m, *nam, *mhead, *mhck; + mbuf_t m, nam, mhead; struct rpc_call *call; struct rpc_reply *reply; - struct uio auio; - int error, rcvflg, timo, secs, len; + int error, timo, secs; + size_t len; static u_int32_t xid = ~0xFF; u_int16_t tport; - struct sockopt sopt; + size_t maxpacket = 1 << 16; /* * Validate address family. * Sorry, this is INET specific... */ - if (sa->sin_family != AF_INET) - return (EAFNOSUPPORT); + if (sa->sin_family != AF_INET) { + return EAFNOSUPPORT; + } /* Free at end if not null. */ nam = mhead = NULL; - if (from_p) - *from_p = 0; /* * Create socket and set its recieve timeout. */ - if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0))) - goto out; + if ((error = sock_socket(AF_INET, sotype, 0, 0, 0, &so))) { + goto out1; + } { struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; - bzero(&sopt, sizeof sopt); - sopt.sopt_dir = SOPT_SET; - sopt.sopt_level = SOL_SOCKET; - sopt.sopt_name = SO_RCVTIMEO; - sopt.sopt_val = &tv; - sopt.sopt_valsize = sizeof tv; - - if (error = sosetopt(so, &sopt)) - goto out; + if ((error = sock_setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))) { + goto out; + } } /* * Enable broadcast if necessary. */ - if (from_p) { + if (from_p && (sotype == SOCK_DGRAM)) { int on = 1; - sopt.sopt_name = SO_BROADCAST; - sopt.sopt_val = &on; - sopt.sopt_valsize = sizeof on; - if (error = sosetopt(so, &sopt)) + if ((error = sock_setsockopt(so, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)))) { goto out; + } } /* @@ -269,19 +276,24 @@ krpc_call(sa, prog, vers, func, data, from_p) * because some NFS servers refuse requests from * non-reserved (non-privileged) ports. */ - m = m_getclr(M_WAIT, MT_SONAME); - sin = mtod(m, struct sockaddr_in *); - sin->sin_len = m->m_len = sizeof(*sin); + if ((error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_SONAME, &m))) { + goto out; + } + sin = mbuf_data(m); + bzero(sin, sizeof(*sin)); + mbuf_setlen(m, sizeof(*sin)); + sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; sin->sin_addr.s_addr = INADDR_ANY; tport = IPPORT_RESERVED; do { tport--; sin->sin_port = htons(tport); - error = sobind(so, mtod(m, struct sockaddr *)); + error = sock_bind(so, (struct sockaddr*)sin); } while (error == EADDRINUSE && - tport > IPPORT_RESERVED / 2); - m_freem(m); + tport > IPPORT_RESERVED / 2); + mbuf_freem(m); + m = NULL; if (error) { printf("bind failed\n"); goto out; @@ -290,37 +302,67 @@ krpc_call(sa, prog, vers, func, data, from_p) /* * Setup socket address for the server. */ - nam = m_get(M_WAIT, MT_SONAME); - if (nam == NULL) { - error = ENOBUFS; + if ((error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_SONAME, &nam))) { goto out; } - sin = mtod(nam, struct sockaddr_in *); - bcopy((caddr_t)sa, (caddr_t)sin, (nam->m_len = sa->sin_len)); + sin = mbuf_data(nam); + mbuf_setlen(nam, sa->sin_len); + bcopy((caddr_t)sa, (caddr_t)sin, sa->sin_len); + + if (sotype == SOCK_STREAM) { + struct timeval tv; + tv.tv_sec = 60; + tv.tv_usec = 0; + error = sock_connect(so, mbuf_data(nam), MSG_DONTWAIT); + if (error && (error != EINPROGRESS)) { + goto out; + } + error = sock_connectwait(so, &tv); + if (error) { + if (error == EINPROGRESS) { + error = ETIMEDOUT; + } + printf("krpc_call: error waiting for TCP socket connect: %d\n", error); + goto out; + } + } /* * Prepend RPC message header. */ m = *data; *data = NULL; -#if DIAGNOSTIC - if ((m->m_flags & M_PKTHDR) == 0) +#if DIAGNOSTIC + if ((mbuf_flags(m) & MBUF_PKTHDR) == 0) { panic("krpc_call: send data w/o pkthdr"); - if (m->m_pkthdr.len < m->m_len) + } + if (mbuf_pkthdr_len(m) < mbuf_len(m)) { panic("krpc_call: pkthdr.len not set"); + } #endif - mhead = m_prepend(m, sizeof(*call), M_WAIT); - if (mhead == NULL) { - error = ENOBUFS; + len = sizeof(*call); + if (sotype == SOCK_STREAM) { + len += 4; /* account for RPC record marker */ + } + mhead = m; + if ((error = mbuf_prepend(&mhead, len, MBUF_WAITOK))) { + goto out; + } + if ((error = mbuf_pkthdr_setrcvif(mhead, NULL))) { goto out; } - mhead->m_pkthdr.len += sizeof(*call); - mhead->m_pkthdr.rcvif = NULL; /* * Fill in the RPC header */ - call = mtod(mhead, struct rpc_call *); + if (sotype == SOCK_STREAM) { + /* first, fill in RPC record marker */ + u_int32_t *recmark = mbuf_data(mhead); + *recmark = htonl(0x80000000 | (mbuf_pkthdr_len(mhead) - 4)); + call = (struct rpc_call *)(recmark + 1); + } else { + call = mbuf_data(mhead); + } bzero((caddr_t)call, sizeof(*call)); xid++; call->rp_xid = htonl(xid); @@ -339,13 +381,21 @@ krpc_call(sa, prog, vers, func, data, from_p) */ timo = 0; for (;;) { + struct msghdr msg; + /* Send RPC request (or re-send). */ - m = m_copym(mhead, 0, M_COPYALL, M_WAIT); - if (m == NULL) { - error = ENOBUFS; + if ((error = mbuf_copym(mhead, 0, MBUF_COPYALL, MBUF_WAITOK, &m))) { goto out; } - error = sosend(so, mtod(nam, struct sockaddr *), NULL, m, NULL, 0); + bzero(&msg, sizeof(msg)); + if (sotype == SOCK_STREAM) { + msg.msg_name = NULL; + msg.msg_namelen = 0; + } else { + msg.msg_name = mbuf_data(nam); + msg.msg_namelen = mbuf_len(nam); + } + error = sock_sendmbuf(so, &msg, m, 0, 0); if (error) { printf("krpc_call: sosend: %d\n", error); goto out; @@ -353,17 +403,12 @@ krpc_call(sa, prog, vers, func, data, from_p) m = NULL; /* Determine new timeout. */ - if (timo < MAX_RESEND_DELAY) + if (timo < MAX_RESEND_DELAY) { timo++; - else - printf("RPC timeout for server " IP_FORMAT "\n", - IP_LIST(&(sin->sin_addr.s_addr))); - - /* - * soreceive is now conditionally using this pointer - * if present, it updates per-proc stats - */ - auio.uio_procp = NULL; + } else { + printf("RPC timeout for server " IP_FORMAT "\n", + IP_LIST(&(sin->sin_addr.s_addr))); + } /* * Wait for up to timo seconds for a reply. @@ -371,41 +416,94 @@ krpc_call(sa, prog, vers, func, data, from_p) */ secs = timo; while (secs > 0) { - if ((from_p) && (*from_p)){ - FREE(*from_p, M_SONAME); - *from_p = NULL; - } + size_t readlen; if (m) { - m_freem(m); + mbuf_freem(m); m = NULL; } - auio.uio_resid = len = 1<<16; - rcvflg = 0; + if (sotype == SOCK_STREAM) { + int maxretries = 60; + struct iovec aio; + aio.iov_base = &len; + aio.iov_len = sizeof(u_int32_t); + bzero(&msg, sizeof(msg)); + msg.msg_iov = &aio; + msg.msg_iovlen = 1; + do { + error = sock_receive(so, &msg, MSG_WAITALL, &readlen); + if ((error == EWOULDBLOCK) && (--maxretries <= 0)) { + error = ETIMEDOUT; + } + } while (error == EWOULDBLOCK); + if (!error && readlen < aio.iov_len) { + /* only log a message if we got a partial word */ + if (readlen != 0) { + printf("short receive (%ld/%ld) from server " IP_FORMAT "\n", + readlen, sizeof(u_int32_t), IP_LIST(&(sin->sin_addr.s_addr))); + } + error = EPIPE; + } + if (error) { + goto out; + } + len = ntohl(len) & ~0x80000000; + /* + * This is SERIOUS! We are out of sync with the sender + * and forcing a disconnect/reconnect is all I can do. + */ + if (len > maxpacket) { + printf("impossible packet length (%ld) from server " IP_FORMAT "\n", + len, IP_LIST(&(sin->sin_addr.s_addr))); + error = EFBIG; + goto out; + } - error = soreceive(so, (struct sockaddr **) from_p, &auio, &m, NULL, &rcvflg); + do { + readlen = len; + error = sock_receivembuf(so, NULL, &m, MSG_WAITALL, &readlen); + } while (error == EWOULDBLOCK); + + if (!error && (len > readlen)) { + printf("short receive (%ld/%ld) from server " IP_FORMAT "\n", + readlen, len, IP_LIST(&(sin->sin_addr.s_addr))); + error = EPIPE; + } + } else { + len = maxpacket; + readlen = len; + bzero(&msg, sizeof(msg)); + msg.msg_name = from_p; + msg.msg_namelen = (from_p == NULL) ? 0 : sizeof(*from_p); + error = sock_receivembuf(so, &msg, &m, 0, &readlen); + } if (error == EWOULDBLOCK) { secs--; continue; } - if (error) + if (error) { goto out; - len -= auio.uio_resid; + } + len = readlen; /* Does the reply contain at least a header? */ - if (len < MIN_REPLY_HDR) + if (len < MIN_REPLY_HDR) { continue; - if (m->m_len < MIN_REPLY_HDR) + } + if (mbuf_len(m) < MIN_REPLY_HDR) { continue; - reply = mtod(m, struct rpc_reply *); + } + reply = mbuf_data(m); /* Is it the right reply? */ - if (reply->rp_direction != htonl(RPC_REPLY)) + if (reply->rp_direction != htonl(RPC_REPLY)) { continue; + } - if (reply->rp_xid != htonl(xid)) + if (reply->rp_xid != htonl(xid)) { continue; + } /* Was RPC accepted? (authorization OK) */ if (reply->rp_astatus != 0) { @@ -423,8 +521,15 @@ krpc_call(sa, prog, vers, func, data, from_p) goto out; } + + if (mbuf_len(m) < REPLY_SIZE) { + error = RPC_SYSTEM_ERR; + } else { + error = ntohl(reply->rp_u.rpu_ok.rp_rstatus); + } + /* Did the call succeed? */ - if ((error = ntohl(reply->rp_u.rpu_ok.rp_rstatus)) != 0) { + if (error != 0) { printf("rpc status=%d\n", error); /* convert rpc error to errno */ switch (error) { @@ -447,15 +552,14 @@ krpc_call(sa, prog, vers, func, data, from_p) goto out; } - goto gotreply; /* break two levels */ - + goto gotreply; /* break two levels */ } /* while secs */ } /* forever send/receive */ error = ETIMEDOUT; goto out; - gotreply: +gotreply: /* * Pull as much as we can into first mbuf, to make @@ -464,18 +568,20 @@ krpc_call(sa, prog, vers, func, data, from_p) * XXX - Should not rely on making the entire reply * contiguous (fix callers instead). -gwr */ -#if DIAGNOSTIC - if ((m->m_flags & M_PKTHDR) == 0) +#if DIAGNOSTIC + if ((mbuf_flags(m) & MBUF_PKTHDR) == 0) { panic("krpc_call: received pkt w/o header?"); + } #endif - len = m->m_pkthdr.len; - if (m->m_len < len) { - m = m_pullup(m, len); - if (m == NULL) { - error = ENOBUFS; + len = mbuf_pkthdr_len(m); + if (sotype == SOCK_STREAM) { + len -= 4; /* the RPC record marker was read separately */ + } + if (mbuf_len(m) < len) { + if ((error = mbuf_pullup(&m, len))) { goto out; } - reply = mtod(m, struct rpc_reply *); + reply = mbuf_data(m); } /* @@ -486,13 +592,20 @@ krpc_call(sa, prog, vers, func, data, from_p) len += ntohl(reply->rp_u.rpu_ok.rp_auth.rp_alen); len = (len + 3) & ~3; /* XXX? */ } - m_adj(m, len); + mbuf_adj(m, len); /* result */ *data = m; - out: - if (nam) m_freem(nam); - if (mhead) m_freem(mhead); - soclose(so); +out: + sock_close(so); +out1: + if (nam) { + mbuf_freem(nam); + } + if (mhead) { + mbuf_freem(mhead); + } return error; } + +#endif /* CONFIG_NFS_CLIENT */