+ case SO_REUSESHAREUID:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error)
+ goto bad;
+ if (optval)
+ so->so_flags |= SOF_REUSESHAREUID;
+ else
+ so->so_flags &= ~SOF_REUSESHAREUID;
+ break;
+#ifdef __APPLE_API_PRIVATE
+ case SO_NOTIFYCONFLICT:
+ if (kauth_cred_issuser(kauth_cred_get()) == 0) {
+ error = EPERM;
+ goto bad;
+ }
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error)
+ goto bad;
+ if (optval)
+ so->so_flags |= SOF_NOTIFYCONFLICT;
+ else
+ so->so_flags &= ~SOF_NOTIFYCONFLICT;
+ break;
+#endif
+ case SO_RESTRICTIONS:
+ if (kauth_cred_issuser(kauth_cred_get()) == 0) {
+ error = EPERM;
+ goto bad;
+ }
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error)
+ goto bad;
+ so->so_restrictions = (optval & (SO_RESTRICT_DENYIN |
+ SO_RESTRICT_DENYOUT | SO_RESTRICT_DENYSET));
+ break;
+
+ case SO_LABEL:
+#if CONFIG_MACF_SOCKET
+ if ((error = sooptcopyin(sopt, &extmac, sizeof (extmac),
+ sizeof (extmac))) != 0)
+ goto bad;
+
+ error = mac_setsockopt_label(proc_ucred(sopt->sopt_p),
+ so, &extmac);
+#else
+ error = EOPNOTSUPP;
+#endif /* MAC_SOCKET */
+ break;
+
+#ifdef __APPLE_API_PRIVATE
+ case SO_UPCALLCLOSEWAIT:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error)
+ goto bad;
+ if (optval)
+ so->so_flags |= SOF_UPCALLCLOSEWAIT;
+ else
+ so->so_flags &= ~SOF_UPCALLCLOSEWAIT;
+ break;
+#endif
+
+ case SO_RANDOMPORT:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error)
+ goto bad;
+ if (optval)
+ so->so_flags |= SOF_BINDRANDOMPORT;
+ else
+ so->so_flags &= ~SOF_BINDRANDOMPORT;
+ break;
+
+ case SO_NP_EXTENSIONS: {
+ struct so_np_extensions sonpx;
+
+ error = sooptcopyin(sopt, &sonpx, sizeof(sonpx), sizeof(sonpx));
+ if (error)
+ goto bad;
+ if (sonpx.npx_mask & ~SONPX_MASK_VALID) {
+ error = EINVAL;
+ goto bad;
+ }
+ /*
+ * Only one bit defined for now
+ */
+ if ((sonpx.npx_mask & SONPX_SETOPTSHUT)) {
+ if ((sonpx.npx_flags & SONPX_SETOPTSHUT))
+ so->so_flags |= SOF_NPX_SETOPTSHUT;
+ else
+ so->so_flags &= ~SOF_NPX_SETOPTSHUT;
+ }
+ break;
+ }
+
+ case SO_TRAFFIC_CLASS: {
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error)
+ goto bad;
+ error = so_set_traffic_class(so, optval);
+ if (error)
+ goto bad;
+ break;
+ }
+
+ case SO_RECV_TRAFFIC_CLASS: {
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error)
+ goto bad;
+ if (optval == 0)
+ so->so_flags &= ~SOF_RECV_TRAFFIC_CLASS;
+ else
+ so->so_flags |= SOF_RECV_TRAFFIC_CLASS;
+ break;
+ }
+
+ case SO_TRAFFIC_CLASS_DBG: {
+ struct so_tcdbg so_tcdbg;
+
+ error = sooptcopyin(sopt, &so_tcdbg,
+ sizeof (struct so_tcdbg), sizeof (struct so_tcdbg));
+ if (error)
+ goto bad;
+ error = so_set_tcdbg(so, &so_tcdbg);
+ if (error)
+ goto bad;
+ break;
+ }
+
+ case SO_PRIVILEGED_TRAFFIC_CLASS:
+ error = priv_check_cred(kauth_cred_get(),
+ PRIV_NET_PRIVILEGED_TRAFFIC_CLASS, 0);
+ if (error)
+ goto bad;
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error)
+ goto bad;
+ if (optval == 0)
+ so->so_flags &= ~SOF_PRIVILEGED_TRAFFIC_CLASS;
+ else
+ so->so_flags |= SOF_PRIVILEGED_TRAFFIC_CLASS;
+ break;
+
+ case SO_DEFUNCTOK:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0 || (so->so_flags & SOF_DEFUNCT)) {
+ if (error == 0)
+ error = EBADF;
+ goto bad;
+ }
+ /*
+ * Any process can set SO_DEFUNCTOK (clear
+ * SOF_NODEFUNCT), but only root can clear
+ * SO_DEFUNCTOK (set SOF_NODEFUNCT).
+ */
+ if (optval == 0 &&
+ kauth_cred_issuser(kauth_cred_get()) == 0) {
+ error = EPERM;
+ goto bad;
+ }
+ if (optval)
+ so->so_flags &= ~SOF_NODEFUNCT;
+ else
+ so->so_flags |= SOF_NODEFUNCT;
+
+ SODEFUNCTLOG(("%s[%d]: so %p [%d,%d] is now marked as "
+ "%seligible for defunct\n", __func__,
+ proc_selfpid(), so, INP_SOCKAF(so),
+ INP_SOCKTYPE(so),
+ (so->so_flags & SOF_NODEFUNCT) ? "not " : ""));
+ break;
+
+ case SO_ISDEFUNCT:
+ /* This option is not settable */
+ error = EINVAL;
+ break;
+
+ case SO_OPPORTUNISTIC:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error == 0)
+ error = so_set_opportunistic(so, optval);
+ break;
+
+ case SO_FLUSH:
+ /* This option is handled by lower layer(s) */
+ error = 0;
+ break;
+
+ case SO_RECV_ANYIF:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error == 0)
+ error = so_set_recv_anyif(so, optval);
+ break;
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ if (error == 0 && so->so_proto && so->so_proto->pr_ctloutput) {
+ (void) ((*so->so_proto->pr_ctloutput)(so, sopt));
+ }
+ }
+bad:
+ socket_unlock(so, 1);
+ return (error);
+}
+
+/* Helper routines for getsockopt */
+int
+sooptcopyout(struct sockopt *sopt, void *buf, size_t len)
+{
+ int error;
+ size_t valsize;
+
+ error = 0;
+
+ /*
+ * Documented get behavior is that we always return a value,
+ * possibly truncated to fit in the user's buffer.
+ * Traditional behavior is that we always tell the user
+ * precisely how much we copied, rather than something useful
+ * like the total amount we had available for her.
+ * Note that this interface is not idempotent; the entire answer must
+ * generated ahead of time.