]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/sys_socket.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / bsd / kern / sys_socket.c
index c276ed03614de11c7b7b1d38337b492f12ae3e27..53e8f07b58167e89ee3b2261893b2fcf61fb962a 100644 (file)
@@ -1,23 +1,29 @@
 /*
- * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
  *
- * @APPLE_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 Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * 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.
+ *
+ * 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.
- * 
- * @APPLE_LICENSE_HEADER_END@
+ * 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_OSREFERENCE_LICENSE_HEADER_END@
  */
 /*
  * Copyright (c) 1982, 1986, 1990, 1993
  *
  *     @(#)sys_socket.c        8.1 (Berkeley) 6/10/93
  */
+/*
+ * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
+ * support for mandatory and extensible security protections.  This notice
+ * is included in support of clause 2.2 (b) of the Apple Public License,
+ * Version 2.0.
+ */
 
 #include <sys/param.h>
 #include <sys/systm.h>
-#include <sys/file.h>
+#include <sys/file_internal.h>
+#include <sys/event.h>
 #include <sys/protosw.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
-#include <sys/filio.h>                 /* XXX */
+#include <sys/filio.h>                  /* XXX */
 #include <sys/sockio.h>
 #include <sys/stat.h>
 #include <sys/uio.h>
 #include <sys/filedesc.h>
+#include <sys/kauth.h>
+#include <sys/signalvar.h>
+#include <sys/vnode.h>
 
 #include <net/if.h>
 #include <net/route.h>
 
-int soo_read __P((struct file *fp, struct uio *uio, 
-               struct ucred *cred, int flags, struct proc *p));
-int soo_write __P((struct file *fp, struct uio *uio, 
-               struct ucred *cred, int flags, struct proc *p));
-int soo_close __P((struct file *fp, struct proc *p));
-
-int soo_select __P((struct file *fp, int which, void * wql, struct proc *p));
+#if CONFIG_MACF
+#include <security/mac_framework.h>
+#endif
 
-struct fileops socketops =
-    { soo_read, soo_write, soo_ioctl, soo_select, soo_close };
+/*
+ * File operations on sockets.
+ */
+static int soo_read(struct fileproc *, struct uio *, int, vfs_context_t ctx);
+static int soo_write(struct fileproc *, struct uio *, int, vfs_context_t ctx);
+static int soo_close(struct fileglob *, vfs_context_t ctx);
+static int soo_drain(struct fileproc *, vfs_context_t ctx);
+
+const struct fileops socketops = {
+       .fo_type     = DTYPE_SOCKET,
+       .fo_read     = soo_read,
+       .fo_write    = soo_write,
+       .fo_ioctl    = soo_ioctl,
+       .fo_select   = soo_select,
+       .fo_close    = soo_close,
+       .fo_drain    = soo_drain,
+       .fo_kqfilter = soo_kqfilter,
+};
 
 /* ARGSUSED */
-int
-soo_read(fp, uio, cred, flags, p)
-       struct file *fp;
-       struct uio *uio;
-       struct ucred *cred;
-       int flags;
-       struct proc *p;
+static int
+soo_read(struct fileproc *fp, struct uio *uio, __unused int flags,
+#if !CONFIG_MACF_SOCKET
+    __unused
+#endif
+    vfs_context_t ctx)
 {
        struct socket *so;
-       struct kextcb *kp;
        int stat;
-       int (*fsoreceive) __P((struct socket *so, 
-                              struct sockaddr **paddr,
-                              struct uio *uio, struct mbuf **mp0,
-                              struct mbuf **controlp, int *flagsp));
+#if CONFIG_MACF_SOCKET
+       int error;
+#endif
 
+       int (*fsoreceive)(struct socket *so2, struct sockaddr **paddr,
+           struct uio *uio2, struct mbuf **mp0, struct mbuf **controlp,
+           int *flagsp);
 
-       thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
+       if ((so = (struct socket *)fp->f_fglob->fg_data) == NULL) {
+               /* This is not a valid open file descriptor */
+               return EBADF;
+       }
 
-        if ((so = (struct socket *)fp->f_data) == NULL) {
-                /* This is not a valid open file descriptor */
-                thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-                return (EBADF);
-        }
+#if CONFIG_MACF_SOCKET
+       error = mac_socket_check_receive(vfs_context_ucred(ctx), so);
+       if (error) {
+               return error;
+       }
+#endif /* CONFIG_MACF_SOCKET */
 
        fsoreceive = so->so_proto->pr_usrreqs->pru_soreceive;
-       if (fsoreceive != soreceive)
-       {       kp = sotokextcb(so);
-               while (kp)
-               {       if (kp->e_soif && kp->e_soif->sf_soreceive)
-                               (*kp->e_soif->sf_soreceive)(so, 0, &uio,
-                                                           0, 0, 0, kp);
-                       kp = kp->e_next;
-               }
 
-       }
-       
        stat = (*fsoreceive)(so, 0, uio, 0, 0, 0);
-       thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
        return stat;
 }
 
 /* ARGSUSED */
-int
-soo_write(fp, uio, cred, flags, p)
-       struct file *fp;
-       struct uio *uio;
-       struct ucred *cred;
-       int flags;
-       struct proc *p;
+static int
+soo_write(struct fileproc *fp, struct uio *uio, __unused int flags,
+    vfs_context_t ctx)
 {
        struct socket *so;
-       int     (*fsosend) __P((struct socket *so, struct sockaddr *addr,
-                               struct uio *uio, struct mbuf *top,
-                               struct mbuf *control, int flags));
-       struct kextcb *kp;
-       int           stat;
+       int stat;
+       int (*fsosend)(struct socket *so2, struct sockaddr *addr,
+           struct uio *uio2, struct mbuf *top, struct mbuf *control,
+           int flags2);
+       proc_t procp;
 
-       thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
+#if CONFIG_MACF_SOCKET
+       int error;
+#endif
 
-        if ((so = (struct socket *)fp->f_data) == NULL) {
-                /* This is not a valid open file descriptor */
-                thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-                return (EBADF);
-        }
+       if ((so = (struct socket *)fp->f_fglob->fg_data) == NULL) {
+               /* This is not a valid open file descriptor */
+               return EBADF;
+       }
 
-       fsosend = so->so_proto->pr_usrreqs->pru_sosend;
-       if (fsosend != sosend)
-       {       kp = sotokextcb(so);
-               while (kp)
-               {       if (kp->e_soif && kp->e_soif->sf_sosend)
-                       (*kp->e_soif->sf_sosend)(so, 0, &uio,
-                                                0, 0, 0, kp);
-                       kp = kp->e_next;
-               }
+#if CONFIG_MACF_SOCKET
+       /* JMM - have to fetch the socket's remote addr */
+       error = mac_socket_check_send(vfs_context_ucred(ctx), so, NULL);
+       if (error) {
+               return error;
        }
+#endif /* CONFIG_MACF_SOCKET */
+
+       fsosend = so->so_proto->pr_usrreqs->pru_sosend;
 
        stat = (*fsosend)(so, 0, uio, 0, 0, 0);
-       thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
 
-        /* Generation of SIGPIPE can be controlled per socket */
-        if (stat == EPIPE && uio->uio_procp && !(so->so_flags & SOF_NOSIGPIPE))
-            psignal(uio->uio_procp, SIGPIPE);
+       /* Generation of SIGPIPE can be controlled per socket */
+       procp = vfs_context_proc(ctx);
+       if (stat == EPIPE && !(so->so_flags & SOF_NOSIGPIPE)) {
+               psignal(procp, SIGPIPE);
+       }
 
-        return stat;
+       return stat;
 }
 
-int
-soo_ioctl(fp, cmd, data, p)
-       struct file *fp;
-       u_long cmd;
-       register caddr_t data;
-       struct proc *p;
+__private_extern__ int
+soioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p)
 {
-       register struct socket *so;
-       struct sockopt sopt;
-       struct kextcb *kp;
-       int    error = 0;
-
-       thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
+       int error = 0;
+       int int_arg;
 
-       if ((so = (struct socket *)fp->f_data) == NULL) {
-               /* This is not a valid open file descriptor */
-               thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-               return (EBADF);
+#if CONFIG_MACF_SOCKET_SUBSET
+       error = mac_socket_check_ioctl(kauth_cred_get(), so, cmd);
+       if (error) {
+               return error;
        }
-
-        kp = sotokextcb(so);
-        sopt.sopt_level = cmd;
-        sopt.sopt_name = (int)data;
-        sopt.sopt_p = p;
-
-       while (kp)
-       {       if (kp->e_soif && kp->e_soif->sf_socontrol)
-                       (*kp->e_soif->sf_socontrol)(so, &sopt, kp);
-               kp = kp->e_next;
+#endif
+
+       socket_lock(so, 1);
+
+       /* call the socket filter's ioctl handler anything but ours */
+       if (IOCGROUP(cmd) != 'i' && IOCGROUP(cmd) != 'r') {
+               switch (cmd) {
+               case SIOCGASSOCIDS32:
+               case SIOCGASSOCIDS64:
+               case SIOCGCONNIDS32:
+               case SIOCGCONNIDS64:
+               case SIOCGCONNINFO32:
+               case SIOCGCONNINFO64:
+               case SIOCSCONNORDER:
+               case SIOCGCONNORDER:
+                       /* don't pass to filter */
+                       break;
+
+               default:
+                       error = sflt_ioctl(so, cmd, data);
+                       if (error != 0) {
+                               goto out;
+                       }
+                       break;
+               }
        }
 
        switch (cmd) {
-
-       case FIONBIO:
-               if (*(int *)data)
+       case FIONBIO:                   /* int */
+               bcopy(data, &int_arg, sizeof(int_arg));
+               if (int_arg) {
                        so->so_state |= SS_NBIO;
-               else
+               } else {
                        so->so_state &= ~SS_NBIO;
+               }
 
-               thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-               return (0);
+               goto out;
 
-       case FIOASYNC:
-               if (*(int *)data) {
+       case FIOASYNC:                  /* int */
+               bcopy(data, &int_arg, sizeof(int_arg));
+               if (int_arg) {
                        so->so_state |= SS_ASYNC;
                        so->so_rcv.sb_flags |= SB_ASYNC;
                        so->so_snd.sb_flags |= SB_ASYNC;
@@ -220,205 +246,272 @@ soo_ioctl(fp, cmd, data, p)
                        so->so_rcv.sb_flags &= ~SB_ASYNC;
                        so->so_snd.sb_flags &= ~SB_ASYNC;
                }
-               thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-               return (0);
-
-       case FIONREAD:
-               *(int *)data = so->so_rcv.sb_cc;
-               thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-               return (0);
-
-       case SIOCSPGRP:
-               so->so_pgid = *(int *)data;
-               thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-               return (0);
-
-       case SIOCGPGRP:
-               *(int *)data = so->so_pgid;
-               thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-               return (0);
-
-       case SIOCATMARK:
-            *(int *)data = (so->so_state&SS_RCVATMARK) != 0;
-               thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-               return (0);
-
-       case SIOCSETOT: {
-            /*
-             * Set socket level options here and then call protocol
-             * specific routine.
-             */
-            struct socket      *cloned_so = NULL;
-            int                                cloned_fd = *(int *)data;
-
-            /* let's make sure it's either -1 or a valid file descriptor */
-            if (cloned_fd != -1) {
-                 struct file     *cloned_fp;
-                 error = getsock(p->p_fd, cloned_fd, &cloned_fp);
-                 if (error) {
-                      thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-                      return (error);
-                 }
-
-                 cloned_so = (struct socket *)cloned_fp->f_data;
-            }
-
-            /* Always set socket non-blocking for OT */
-            fp->f_flag |= FNONBLOCK;
-            so->so_state |= SS_NBIO;
-            so->so_options |= SO_DONTTRUNC | SO_WANTMORE;
-          so->so_flags |= SOF_NOSIGPIPE;
-
-            if (cloned_so && so != cloned_so) {
-                 /* Flags options */
-                 so->so_options |= cloned_so->so_options & ~SO_ACCEPTCONN;
-
-            /* SO_LINGER */
-            if (so->so_options & SO_LINGER)
-                so->so_linger = cloned_so->so_linger;
-
-            /* SO_SNDBUF, SO_RCVBUF */
-                 if (cloned_so->so_snd.sb_hiwat > 0) {
-                      if (sbreserve(&so->so_snd, cloned_so->so_snd.sb_hiwat) == 0) {
-                           error = ENOBUFS;
-                           thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-                           return (error);
-                      }
-                 }
-                 if (cloned_so->so_rcv.sb_hiwat > 0) {
-                      if (sbreserve(&so->so_rcv, cloned_so->so_rcv.sb_hiwat) == 0) {
-                           error = ENOBUFS;
-                           thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-                           return (error);
-                      }
-                 }
-
-                 /* SO_SNDLOWAT, SO_RCVLOWAT */
-                 so->so_snd.sb_lowat =
-                      (cloned_so->so_snd.sb_lowat > so->so_snd.sb_hiwat) ?
-                      so->so_snd.sb_hiwat : cloned_so->so_snd.sb_lowat;
-                 so->so_rcv.sb_lowat =
-                      (cloned_so->so_rcv.sb_lowat > so->so_rcv.sb_hiwat) ?
-                      so->so_rcv.sb_hiwat : cloned_so->so_rcv.sb_lowat;
-
-            /* SO_SNDTIMEO, SO_RCVTIMEO */
-                 so->so_snd.sb_timeo = cloned_so->so_snd.sb_timeo;
-                 so->so_rcv.sb_timeo = cloned_so->so_rcv.sb_timeo;
-            }
-
-            error = (*so->so_proto->pr_usrreqs->pru_control)(so, cmd, data, 0, p);
-            /* Just ignore protocols that do not understand it */
-            if (error == EOPNOTSUPP)
-                 error = 0;
-
-            thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-            return (error);
-        }
+               goto out;
+
+       case FIONREAD:                  /* int */
+               bcopy(&so->so_rcv.sb_cc, data, sizeof(u_int32_t));
+               goto out;
+
+       case SIOCSPGRP:                 /* int */
+               bcopy(data, &so->so_pgid, sizeof(pid_t));
+               goto out;
+
+       case SIOCGPGRP:                 /* int */
+               bcopy(&so->so_pgid, data, sizeof(pid_t));
+               goto out;
+
+       case SIOCATMARK:                /* int */
+               int_arg = (so->so_state & SS_RCVATMARK) != 0;
+               bcopy(&int_arg, data, sizeof(int_arg));
+               goto out;
+
+       case SIOCSETOT:                 /* int; deprecated */
+               error = EOPNOTSUPP;
+               goto out;
+
+       case SIOCGASSOCIDS32:           /* so_aidreq32 */
+       case SIOCGASSOCIDS64:           /* so_aidreq64 */
+       case SIOCGCONNIDS32:            /* so_cidreq32 */
+       case SIOCGCONNIDS64:            /* so_cidreq64 */
+       case SIOCGCONNINFO32:           /* so_cinforeq32 */
+       case SIOCGCONNINFO64:           /* so_cinforeq64 */
+       case SIOCSCONNORDER:            /* so_cordreq */
+       case SIOCGCONNORDER:            /* so_cordreq */
+               error = (*so->so_proto->pr_usrreqs->pru_control)(so,
+                   cmd, data, NULL, p);
+               goto out;
        }
+
        /*
         * Interface/routing/protocol specific ioctls:
         * interface and routing ioctls should have a
         * different entry since a socket's unnecessary
         */
-       if (IOCGROUP(cmd) == 'i')
-            error = ifioctl(so, cmd, data, p);
-       else 
-            if (IOCGROUP(cmd) == 'r')
-                 error = rtioctl(cmd, data, p);
-            else
-                 error = (*so->so_proto->pr_usrreqs->pru_control)(so, cmd, data, 0, p);
-
-       thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
+       if (IOCGROUP(cmd) == 'i') {
+               error = ifioctllocked(so, cmd, data, p);
+       } else {
+               if (IOCGROUP(cmd) == 'r') {
+                       error = rtioctl(cmd, data, p);
+               } else {
+                       error = (*so->so_proto->pr_usrreqs->pru_control)(so,
+                           cmd, data, NULL, p);
+               }
+       }
+
+out:
+       socket_unlock(so, 1);
+
+       if (error == EJUSTRETURN) {
+               error = 0;
+       }
+
        return error;
 }
 
 int
-soo_select(fp, which, wql, p)
-       struct file *fp;
-       int which;
-       void * wql;
-       struct proc *p;
+soo_ioctl(struct fileproc *fp, u_long cmd, caddr_t data, vfs_context_t ctx)
+{
+       struct socket *so;
+       proc_t procp = vfs_context_proc(ctx);
+
+       if ((so = (struct socket *)fp->f_fglob->fg_data) == NULL) {
+               /* This is not a valid open file descriptor */
+               return EBADF;
+       }
+
+       return soioctl(so, cmd, data, procp);
+}
+
+int
+soo_select(struct fileproc *fp, int which, void *wql, vfs_context_t ctx)
 {
-       register struct socket *so = (struct socket *)fp->f_data;
-       register int s = splnet();
-       int retnum=0;
+       struct socket *so = (struct socket *)fp->f_fglob->fg_data;
+       int retnum = 0;
+       proc_t procp;
+
+       if (so == NULL || so == (struct socket *)-1) {
+               return 0;
+       }
 
+       procp = vfs_context_proc(ctx);
+
+#if CONFIG_MACF_SOCKET
+       if (mac_socket_check_select(vfs_context_ucred(ctx), so, which) != 0) {
+               return 0;
+       }
+#endif /* CONFIG_MACF_SOCKET */
 
-       switch (which) {
 
+       socket_lock(so, 1);
+       switch (which) {
        case FREAD:
                so->so_rcv.sb_flags |= SB_SEL;
                if (soreadable(so)) {
-                       splx(s);
                        retnum = 1;
                        so->so_rcv.sb_flags &= ~SB_SEL;
                        goto done;
                }
-               selrecord(p, &so->so_rcv.sb_sel, wql);
+               selrecord(procp, &so->so_rcv.sb_sel, wql);
                break;
 
        case FWRITE:
                so->so_snd.sb_flags |= SB_SEL;
                if (sowriteable(so)) {
-                       splx(s);
                        retnum = 1;
                        so->so_snd.sb_flags &= ~SB_SEL;
                        goto done;
                }
-               selrecord(p, &so->so_snd.sb_sel, wql);
+               selrecord(procp, &so->so_snd.sb_sel, wql);
                break;
 
        case 0:
                so->so_rcv.sb_flags |= SB_SEL;
                if (so->so_oobmark || (so->so_state & SS_RCVATMARK)) {
-                       splx(s);
                        retnum = 1;
                        so->so_rcv.sb_flags &= ~SB_SEL;
                        goto done;
                }
-               selrecord(p, &so->so_rcv.sb_sel, wql);
+               selrecord(procp, &so->so_rcv.sb_sel, wql);
                break;
        }
-       splx(s);
+
 done:
-       return (retnum);
+       socket_unlock(so, 1);
+       return retnum;
 }
 
-
 int
-soo_stat(so, ub)
-       register struct socket *so;
-       register struct stat *ub;
+soo_stat(struct socket *so, void *ub, int isstat64)
 {
-       int stat;
+       int ret;
+       /* warning avoidance ; protected by isstat64 */
+       struct stat *sb = (struct stat *)0;
+       /* warning avoidance ; protected by isstat64 */
+       struct stat64 *sb64 = (struct stat64 *)0;
+
+#if CONFIG_MACF_SOCKET_SUBSET
+       ret = mac_socket_check_stat(kauth_cred_get(), so);
+       if (ret) {
+               return ret;
+       }
+#endif
+
+       if (isstat64 != 0) {
+               sb64 = (struct stat64 *)ub;
+               bzero((caddr_t)sb64, sizeof(*sb64));
+       } else {
+               sb = (struct stat *)ub;
+               bzero((caddr_t)sb, sizeof(*sb));
+       }
 
-       /*
-        * DANGER: by the time we get the network funnel the socket 
-        * may have been closed
-        */
-       thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
-       bzero((caddr_t)ub, sizeof (*ub));
-       ub->st_mode = S_IFSOCK;
-       stat = (*so->so_proto->pr_usrreqs->pru_sense)(so, ub);
-       thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
-       return stat;
+       socket_lock(so, 1);
+       if (isstat64 != 0) {
+               sb64->st_mode = S_IFSOCK;
+               if ((so->so_state & SS_CANTRCVMORE) == 0 ||
+                   so->so_rcv.sb_cc != 0) {
+                       sb64->st_mode |= S_IRUSR | S_IRGRP | S_IROTH;
+               }
+               if ((so->so_state & SS_CANTSENDMORE) == 0) {
+                       sb64->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+               }
+               sb64->st_size = so->so_rcv.sb_cc - so->so_rcv.sb_ctl;
+               sb64->st_uid = kauth_cred_getuid(so->so_cred);
+               sb64->st_gid = kauth_cred_getgid(so->so_cred);
+       } else {
+               sb->st_mode = S_IFSOCK;
+               if ((so->so_state & SS_CANTRCVMORE) == 0 ||
+                   so->so_rcv.sb_cc != 0) {
+                       sb->st_mode |= S_IRUSR | S_IRGRP | S_IROTH;
+               }
+               if ((so->so_state & SS_CANTSENDMORE) == 0) {
+                       sb->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+               }
+               sb->st_size = so->so_rcv.sb_cc - so->so_rcv.sb_ctl;
+               sb->st_uid = kauth_cred_getuid(so->so_cred);
+               sb->st_gid = kauth_cred_getgid(so->so_cred);
+       }
+
+       ret = (*so->so_proto->pr_usrreqs->pru_sense)(so, ub, isstat64);
+       socket_unlock(so, 1);
+       return ret;
 }
 
 /* ARGSUSED */
-int
-soo_close(fp, p)
-       struct file *fp;
-       struct proc *p;
+static int
+soo_close(struct fileglob *fg, __unused vfs_context_t ctx)
 {
        int error = 0;
+       struct socket *sp;
 
-       thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
+       sp = (struct socket *)fg->fg_data;
+       fg->fg_data = NULL;
 
-       if (fp->f_data)
-            error = soclose((struct socket *)fp->f_data);
+       if (sp) {
+               error = soclose(sp);
+       }
+
+       return error;
+}
+
+static int
+soo_drain(struct fileproc *fp, __unused vfs_context_t ctx)
+{
+       int error = 0;
+       struct socket *so = (struct socket *)fp->f_fglob->fg_data;
+
+       if (so) {
+               socket_lock(so, 1);
+               so->so_state |= SS_DRAINING;
+
+               wakeup((caddr_t)&so->so_timeo);
+               sorwakeup(so);
+               sowwakeup(so);
+               soevent(so, SO_FILT_HINT_LOCKED);
+
+               socket_unlock(so, 1);
+       }
+
+       return error;
+}
 
-       thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
+/*
+ * 's' group ioctls.
+ *
+ * The switch statement below does nothing at runtime, as it serves as a
+ * compile time check to ensure that all of the socket 's' ioctls (those
+ * in the 's' group going thru soo_ioctl) that are made available by the
+ * networking stack is unique.  This works as long as this routine gets
+ * updated each time a new interface ioctl gets added.
+ *
+ * Any failures at compile time indicates duplicated ioctl values.
+ */
+static __attribute__((unused)) void
+soioctl_cassert(void)
+{
+       /*
+        * This is equivalent to _CASSERT() and the compiler wouldn't
+        * generate any instructions, thus for compile time only.
+        */
+       switch ((u_long)0) {
+       case 0:
 
-       fp->f_data = 0;
-       return (error);
+       /* bsd/sys/sockio.h */
+       case SIOCSHIWAT:
+       case SIOCGHIWAT:
+       case SIOCSLOWAT:
+       case SIOCGLOWAT:
+       case SIOCATMARK:
+       case SIOCSPGRP:
+       case SIOCGPGRP:
+       case SIOCSETOT:
+       case SIOCGASSOCIDS32:
+       case SIOCGASSOCIDS64:
+       case SIOCGCONNIDS32:
+       case SIOCGCONNIDS64:
+       case SIOCGCONNINFO32:
+       case SIOCGCONNINFO64:
+       case SIOCSCONNORDER:
+       case SIOCGCONNORDER:
+               ;
+       }
 }