X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..15129b1c8dbb3650c63b70adb1cad9af601c6c17:/bsd/nfs/krpc_subr.c diff --git a/bsd/nfs/krpc_subr.c b/bsd/nfs/krpc_subr.c index 98a7eee68..8ded0f04b 100644 --- a/bsd/nfs/krpc_subr.c +++ b/bsd/nfs/krpc_subr.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2008 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * 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. 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. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * 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, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * 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 */ /* @@ -64,7 +70,7 @@ #include #include #include -#include +#include #include #include #include @@ -140,10 +146,10 @@ 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(sin, prog, vers, proto, portp) + struct sockaddr_in *sin; /* server address */ + u_int prog, vers, proto; /* host order */ + u_int16_t *portp; /* network order */ { struct sdata { u_int32_t prog; /* call program */ @@ -155,7 +161,7 @@ krpc_portmap(sin, prog, vers, portp) u_int16_t pad; u_int16_t port; } *rdata; - struct mbuf *m; + mbuf_t m; int error; /* The portmapper port is fixed. */ @@ -164,30 +170,32 @@ 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); + error = krpc_call(sin, SOCK_DGRAM, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT, &m, NULL); if (error) return error; - rdata = mtod(m, struct rdata *); + rdata = mbuf_data(m); *portp = rdata->port; - m_freem(m); - return 0; + if (!rdata->port) + error = EPROGUNAVAIL; + + mbuf_freem(m); + return (error); } /* @@ -196,22 +204,22 @@ 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) +krpc_call(sa, sotype, 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 */ + u_int sotype, prog, vers, 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. @@ -222,27 +230,20 @@ krpc_call(sa, prog, vers, func, data, from_p) /* 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_level = SOL_SOCKET; - sopt.sopt_name = SO_RCVTIMEO; - sopt.sopt_val = &tv; - sopt.sopt_valsize = sizeof tv; - if (error = sosetopt(so, &sopt)) + if ((error = sock_setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))) goto out; } @@ -251,12 +252,9 @@ krpc_call(sa, prog, vers, func, data, from_p) * 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; } @@ -265,19 +263,23 @@ 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); + mbuf_freem(m); + m = NULL; if (error) { printf("bind failed\n"); goto out; @@ -286,13 +288,27 @@ 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 = 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; + } } - sin = mtod(nam, struct sockaddr_in *); - bcopy((caddr_t)sa, (caddr_t)sin, (nam->m_len = sa->sin_len)); /* * Prepend RPC message header. @@ -300,23 +316,31 @@ krpc_call(sa, prog, vers, func, data, from_p) m = *data; *data = NULL; #if DIAGNOSTIC - if ((m->m_flags & M_PKTHDR) == 0) + 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); @@ -335,13 +359,20 @@ 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; + 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 = sosend(so, mtod(nam, struct sockaddr *), NULL, m, NULL, 0); + error = sock_sendmbuf(so, &msg, m, 0, 0); if (error) { printf("krpc_call: sosend: %d\n", error); goto out; @@ -361,19 +392,64 @@ 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; - - error = soreceive(so, (struct sockaddr **) from_p, &auio, &m, NULL, &rcvflg); + 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; + } + + 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--; @@ -381,14 +457,14 @@ krpc_call(sa, prog, vers, func, data, from_p) } if (error) goto out; - len -= auio.uio_resid; + len = readlen; /* Does the reply contain at least a header? */ 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)) @@ -396,18 +472,45 @@ krpc_call(sa, prog, vers, func, data, from_p) if (reply->rp_xid != htonl(xid)) continue; - + /* Was RPC accepted? (authorization OK) */ if (reply->rp_astatus != 0) { error = ntohl(reply->rp_u.rpu_errno); printf("rpc denied, error=%d\n", error); - continue; + /* convert rpc error to errno */ + switch (error) { + case RPC_MISMATCH: + error = ERPCMISMATCH; + break; + case RPC_AUTHERR: + error = EAUTH; + break; + } + goto out; } /* Did the call succeed? */ if ((error = ntohl(reply->rp_u.rpu_ok.rp_rstatus)) != 0) { printf("rpc status=%d\n", error); - continue; + /* convert rpc error to errno */ + switch (error) { + case RPC_PROGUNAVAIL: + error = EPROGUNAVAIL; + break; + case RPC_PROGMISMATCH: + error = EPROGMISMATCH; + break; + case RPC_PROCUNAVAIL: + error = EPROCUNAVAIL; + break; + case RPC_GARBAGE: + error = EINVAL; + break; + case RPC_SYSTEM_ERR: + error = EIO; + break; + } + goto out; } goto gotreply; /* break two levels */ @@ -428,17 +531,16 @@ krpc_call(sa, prog, vers, func, data, from_p) * contiguous (fix callers instead). -gwr */ #if DIAGNOSTIC - if ((m->m_flags & M_PKTHDR) == 0) + 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); } /* @@ -449,13 +551,14 @@ 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); + sock_close(so); +out1: + if (nam) mbuf_freem(nam); + if (mhead) mbuf_freem(mhead); return error; }