]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/sys_socket.c
xnu-6153.41.3.tar.gz
[apple/xnu.git] / bsd / kern / sys_socket.c
index 471cac76a278ef07be0763445f9b54e8146a08f8..53e8f07b58167e89ee3b2261893b2fcf61fb962a 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
  *
  * @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
  * 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,
@@ -22,7 +22,7 @@
  * 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@
  */
 /*
@@ -73,7 +73,7 @@
 #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>
@@ -97,24 +97,24 @@ 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);
 
-/* TODO: these should be in header file */
-extern int soo_ioctl(struct fileproc *, u_long, caddr_t, vfs_context_t ctx);
-extern int soo_stat(struct socket *, void *, int);
-extern int soo_select(struct fileproc *, int, void *, vfs_context_t ctx);
-extern int soo_kqfilter(struct fileproc *, struct knote *, vfs_context_t ctx);
-
-struct fileops socketops = {
-       soo_read, soo_write, soo_ioctl, soo_select, soo_close,
-       soo_kqfilter, soo_drain
+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 */
 static int
 soo_read(struct fileproc *fp, struct uio *uio, __unused int flags,
 #if !CONFIG_MACF_SOCKET
-       __unused
+    __unused
 #endif
-       vfs_context_t ctx)
+    vfs_context_t ctx)
 {
        struct socket *so;
        int stat;
@@ -128,26 +128,26 @@ soo_read(struct fileproc *fp, struct uio *uio, __unused int flags,
 
        if ((so = (struct socket *)fp->f_fglob->fg_data) == NULL) {
                /* This is not a valid open file descriptor */
-               return (EBADF);
+               return EBADF;
        }
 
 #if CONFIG_MACF_SOCKET
        error = mac_socket_check_receive(vfs_context_ucred(ctx), so);
-       if (error)
-               return (error);
+       if (error) {
+               return error;
+       }
 #endif /* CONFIG_MACF_SOCKET */
 
-//###LD will have to change
        fsoreceive = so->so_proto->pr_usrreqs->pru_soreceive;
 
        stat = (*fsoreceive)(so, 0, uio, 0, 0, 0);
-       return (stat);
+       return stat;
 }
 
 /* ARGSUSED */
 static int
 soo_write(struct fileproc *fp, struct uio *uio, __unused int flags,
-       vfs_context_t ctx)
+    vfs_context_t ctx)
 {
        struct socket *so;
        int stat;
@@ -162,14 +162,15 @@ soo_write(struct fileproc *fp, struct uio *uio, __unused int flags,
 
        if ((so = (struct socket *)fp->f_fglob->fg_data) == NULL) {
                /* This is not a valid open file descriptor */
-               return (EBADF);
+               return EBADF;
        }
 
 #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);
+       if (error) {
+               return error;
+       }
 #endif /* CONFIG_MACF_SOCKET */
 
        fsosend = so->so_proto->pr_usrreqs->pru_sosend;
@@ -178,59 +179,65 @@ soo_write(struct fileproc *fp, struct uio *uio, __unused int flags,
 
        /* Generation of SIGPIPE can be controlled per socket */
        procp = vfs_context_proc(ctx);
-       if (stat == EPIPE && !(so->so_flags & SOF_NOSIGPIPE))
+       if (stat == EPIPE && !(so->so_flags & SOF_NOSIGPIPE)) {
                psignal(procp, SIGPIPE);
+       }
 
-       return (stat);
+       return stat;
 }
 
 __private_extern__ int
 soioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p)
 {
        int error = 0;
-       int dropsockref = -1;
+       int int_arg;
+
+#if CONFIG_MACF_SOCKET_SUBSET
+       error = mac_socket_check_ioctl(kauth_cred_get(), so, cmd);
+       if (error) {
+               return error;
+       }
+#endif
 
        socket_lock(so, 1);
 
-       /* Call the socket filter's ioctl handler for most ioctls */
+       /* call the socket filter's ioctl handler anything but ours */
        if (IOCGROUP(cmd) != 'i' && IOCGROUP(cmd) != 'r') {
-               int filtered = 0;
-               struct socket_filter_entry *filter;
-
-               for (filter = so->so_filt; filter && error == 0;
-                   filter = filter->sfe_next_onsocket) {
-                       if (filter->sfe_filter->sf_filter.sf_ioctl) {
-                               if (filtered == 0) {
-                                       sflt_use(so);
-                                       socket_unlock(so, 0);
-                                       filtered = 1;
-                               }
-                               error = filter->sfe_filter->sf_filter.
-                                   sf_ioctl(filter->sfe_cookie, so, cmd, data);
+               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;
                }
-
-               if (filtered) {
-                       socket_lock(so, 0);
-                       sflt_unuse(so);
-               }
-
-               if (error != 0)
-                       goto out;
        }
 
        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;
+               }
 
                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;
@@ -241,91 +248,40 @@ soioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p)
                }
                goto out;
 
-       case FIONREAD:
-               *(int *)data = so->so_rcv.sb_cc;
+       case FIONREAD:                  /* int */
+               bcopy(&so->so_rcv.sb_cc, data, sizeof(u_int32_t));
                goto out;
 
-       case SIOCSPGRP:
-               so->so_pgid = *(int *)data;
+       case SIOCSPGRP:                 /* int */
+               bcopy(data, &so->so_pgid, sizeof(pid_t));
                goto out;
 
-       case SIOCGPGRP:
-               *(int *)data = so->so_pgid;
+       case SIOCGPGRP:                 /* int */
+               bcopy(&so->so_pgid, data, sizeof(pid_t));
                goto out;
 
-       case SIOCATMARK:
-               *(int *)data = (so->so_state&SS_RCVATMARK) != 0;
+       case SIOCATMARK:                /* int */
+               int_arg = (so->so_state & SS_RCVATMARK) != 0;
+               bcopy(&int_arg, data, sizeof(int_arg));
                goto out;
 
-       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) {
-                       error = file_socket(cloned_fd, &cloned_so);
-                       if (error) {
-                               goto out;
-                       }
-                       dropsockref = cloned_fd;
-               }
-
-               /* Always set socket non-blocking for OT */
-               so->so_state |= SS_NBIO;
-               so->so_options |= SO_DONTTRUNC | SO_WANTMORE;
-               so->so_flags |= SOF_NOSIGPIPE | SOF_NPX_SETOPTSHUT;
-
-               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;
-                                       goto out;
-                               }
-                       }
-                       if (cloned_so->so_rcv.sb_hiwat > 0) {
-                               if (sbreserve(&so->so_rcv,
-                                   cloned_so->so_rcv.sb_hiwat) == 0) {
-                                       error = ENOBUFS;
-                                       goto out;
-                               }
-                       }
-
-                       /* 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;
+       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
@@ -334,42 +290,36 @@ soioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p)
        if (IOCGROUP(cmd) == 'i') {
                error = ifioctllocked(so, cmd, data, p);
        } else {
-               if (IOCGROUP(cmd) == 'r')
+               if (IOCGROUP(cmd) == 'r') {
                        error = rtioctl(cmd, data, p);
-               else
+               } else {
                        error = (*so->so_proto->pr_usrreqs->pru_control)(so,
-                           cmd, data, 0, p);
+                           cmd, data, NULL, p);
+               }
        }
 
 out:
-       if (dropsockref != -1)
-               file_drop(dropsockref);
        socket_unlock(so, 1);
 
-       if (error == EJUSTRETURN)
+       if (error == EJUSTRETURN) {
                error = 0;
+       }
 
-       return (error);
+       return error;
 }
 
 int
 soo_ioctl(struct fileproc *fp, u_long cmd, caddr_t data, vfs_context_t ctx)
 {
        struct socket *so;
-       int error;
        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 EBADF;
        }
 
-       error = soioctl(so, cmd, data, procp);
-
-       if (error == 0 && cmd == SIOCSETOT)
-               fp->f_fglob->fg_flag |= FNONBLOCK;
-
-       return (error);
+       return soioctl(so, cmd, data, procp);
 }
 
 int
@@ -379,20 +329,21 @@ soo_select(struct fileproc *fp, int which, void *wql, vfs_context_t ctx)
        int retnum = 0;
        proc_t procp;
 
-       if (so == NULL || so == (struct socket *)-1)
-               return (0);
-       
+       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);
+       if (mac_socket_check_select(vfs_context_ucred(ctx), so, which) != 0) {
+               return 0;
+       }
 #endif /* CONFIG_MACF_SOCKET */
 
 
        socket_lock(so, 1);
        switch (which) {
-
        case FREAD:
                so->so_rcv.sb_flags |= SB_SEL;
                if (soreadable(so)) {
@@ -426,7 +377,7 @@ soo_select(struct fileproc *fp, int which, void *wql, vfs_context_t ctx)
 
 done:
        socket_unlock(so, 1);
-       return (retnum);
+       return retnum;
 }
 
 int
@@ -438,46 +389,51 @@ soo_stat(struct socket *so, void *ub, int isstat64)
        /* warning avoidance ; protected by isstat64 */
        struct stat64 *sb64 = (struct stat64 *)0;
 
-#if CONFIG_MACF_SOCKET
+#if CONFIG_MACF_SOCKET_SUBSET
        ret = mac_socket_check_stat(kauth_cred_get(), so);
-       if (ret)
-               return (ret);
+       if (ret) {
+               return ret;
+       }
 #endif
 
        if (isstat64 != 0) {
                sb64 = (struct stat64 *)ub;
-               bzero((caddr_t)sb64, sizeof (*sb64));
+               bzero((caddr_t)sb64, sizeof(*sb64));
        } else {
                sb = (struct stat *)ub;
-               bzero((caddr_t)sb, sizeof (*sb));
+               bzero((caddr_t)sb, sizeof(*sb));
        }
 
        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)
+                   so->so_rcv.sb_cc != 0) {
                        sb64->st_mode |= S_IRUSR | S_IRGRP | S_IROTH;
-               if ((so->so_state & SS_CANTSENDMORE) == 0)
+               }
+               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 = so->so_uid;
-               sb64->st_gid = -1;      /* XXX -- what else to do? */
+               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)
+                   so->so_rcv.sb_cc != 0) {
                        sb->st_mode |= S_IRUSR | S_IRGRP | S_IROTH;
-               if ((so->so_state & SS_CANTSENDMORE) == 0)
+               }
+               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 = so->so_uid;
-               sb->st_gid = -1;        /* XXX -- what else to do? */
+               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);
+       return ret;
 }
 
 /* ARGSUSED */
@@ -490,10 +446,11 @@ soo_close(struct fileglob *fg, __unused vfs_context_t ctx)
        sp = (struct socket *)fg->fg_data;
        fg->fg_data = NULL;
 
-       if (sp)
+       if (sp) {
                error = soclose(sp);
+       }
 
-       return (error);
+       return error;
 }
 
 static int
@@ -509,9 +466,52 @@ soo_drain(struct fileproc *fp, __unused vfs_context_t ctx)
                wakeup((caddr_t)&so->so_timeo);
                sorwakeup(so);
                sowwakeup(so);
+               soevent(so, SO_FILT_HINT_LOCKED);
 
                socket_unlock(so, 1);
        }
 
-       return (error);
+       return error;
+}
+
+/*
+ * '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:
+
+       /* 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:
+               ;
+       }
 }