+done:
+ return (error);
+}
+
+void
+sowflush(struct socket *so)
+{
+ struct sockbuf *sb = &so->so_snd;
+#ifdef notyet
+ lck_mtx_t *mutex_held;
+ /*
+ * XXX: This code is currently commented out, because we may get here
+ * as part of sofreelastref(), and at that time, pr_getlock() may no
+ * longer be able to return us the lock; this will be fixed in future.
+ */
+ if (so->so_proto->pr_getlock != NULL)
+ mutex_held = (*so->so_proto->pr_getlock)(so, 0);
+ else
+ mutex_held = so->so_proto->pr_domain->dom_mtx;
+
+ lck_mtx_assert(mutex_held, LCK_MTX_ASSERT_OWNED);
+#endif /* notyet */
+
+ /*
+ * Obtain lock on the socket buffer (SB_LOCK). This is required
+ * to prevent the socket buffer from being unexpectedly altered
+ * while it is used by another thread in socket send/receive.
+ *
+ * sblock() must not fail here, hence the assertion.
+ */
+ (void) sblock(sb, SBL_WAIT | SBL_NOINTR | SBL_IGNDEFUNCT);
+ VERIFY(sb->sb_flags & SB_LOCK);
+
+ sb->sb_flags &= ~(SB_SEL|SB_UPCALL);
+ sb->sb_flags |= SB_DROP;
+ sb->sb_upcall = NULL;
+ sb->sb_upcallarg = NULL;
+
+ sbunlock(sb, TRUE); /* keep socket locked */
+
+ selthreadclear(&sb->sb_sel);
+ sbrelease(sb);
+}
+
+void
+sorflush(struct socket *so)
+{
+ struct sockbuf *sb = &so->so_rcv;
+ struct protosw *pr = so->so_proto;
+ struct sockbuf asb;
+#ifdef notyet
+ lck_mtx_t *mutex_held;
+ /*
+ * XXX: This code is currently commented out, because we may get here
+ * as part of sofreelastref(), and at that time, pr_getlock() may no
+ * longer be able to return us the lock; this will be fixed in future.
+ */
+ if (so->so_proto->pr_getlock != NULL)
+ mutex_held = (*so->so_proto->pr_getlock)(so, 0);
+ else
+ mutex_held = so->so_proto->pr_domain->dom_mtx;
+
+ lck_mtx_assert(mutex_held, LCK_MTX_ASSERT_OWNED);
+#endif /* notyet */
+
+ sflt_notify(so, sock_evt_flush_read, NULL);
+
+ socantrcvmore(so);
+
+ /*
+ * Obtain lock on the socket buffer (SB_LOCK). This is required
+ * to prevent the socket buffer from being unexpectedly altered
+ * while it is used by another thread in socket send/receive.
+ *
+ * sblock() must not fail here, hence the assertion.
+ */
+ (void) sblock(sb, SBL_WAIT | SBL_NOINTR | SBL_IGNDEFUNCT);
+ VERIFY(sb->sb_flags & SB_LOCK);
+
+ /*
+ * Copy only the relevant fields from "sb" to "asb" which we
+ * need for sbrelease() to function. In particular, skip
+ * sb_sel as it contains the wait queue linkage, which would
+ * wreak havoc if we were to issue selthreadclear() on "asb".
+ * Make sure to not carry over SB_LOCK in "asb", as we need
+ * to acquire it later as part of sbrelease().
+ */
+ bzero(&asb, sizeof (asb));
+ asb.sb_cc = sb->sb_cc;
+ asb.sb_hiwat = sb->sb_hiwat;
+ asb.sb_mbcnt = sb->sb_mbcnt;
+ asb.sb_mbmax = sb->sb_mbmax;
+ asb.sb_ctl = sb->sb_ctl;
+ asb.sb_lowat = sb->sb_lowat;
+ asb.sb_mb = sb->sb_mb;
+ asb.sb_mbtail = sb->sb_mbtail;
+ asb.sb_lastrecord = sb->sb_lastrecord;
+ asb.sb_so = sb->sb_so;
+ asb.sb_flags = sb->sb_flags;
+ asb.sb_flags &= ~(SB_LOCK|SB_SEL|SB_KNOTE|SB_UPCALL);
+ asb.sb_flags |= SB_DROP;
+
+ /*
+ * Ideally we'd bzero() these and preserve the ones we need;
+ * but to do that we'd need to shuffle things around in the
+ * sockbuf, and we can't do it now because there are KEXTS
+ * that are directly referring to the socket structure.
+ *
+ * Setting SB_DROP acts as a barrier to prevent further appends.
+ * Clearing SB_SEL is done for selthreadclear() below.
+ */
+ sb->sb_cc = 0;
+ sb->sb_hiwat = 0;
+ sb->sb_mbcnt = 0;
+ sb->sb_mbmax = 0;
+ sb->sb_ctl = 0;
+ sb->sb_lowat = 0;
+ sb->sb_mb = NULL;
+ sb->sb_mbtail = NULL;
+ sb->sb_lastrecord = NULL;
+ sb->sb_timeo.tv_sec = 0;
+ sb->sb_timeo.tv_usec = 0;
+ sb->sb_upcall = NULL;
+ sb->sb_upcallarg = NULL;
+ sb->sb_flags &= ~(SB_SEL|SB_UPCALL);
+ sb->sb_flags |= SB_DROP;
+
+ sbunlock(sb, TRUE); /* keep socket locked */
+
+ /*
+ * Note that selthreadclear() is called on the original "sb" and
+ * not the local "asb" because of the way wait queue linkage is
+ * implemented. Given that selwakeup() may be triggered, SB_SEL
+ * should no longer be set (cleared above.)
+ */
+ selthreadclear(&sb->sb_sel);
+
+ if ((pr->pr_flags & PR_RIGHTS) && pr->pr_domain->dom_dispose)
+ (*pr->pr_domain->dom_dispose)(asb.sb_mb);
+
+ sbrelease(&asb);
+}
+
+/*
+ * Perhaps this routine, and sooptcopyout(), below, ought to come in
+ * an additional variant to handle the case where the option value needs
+ * to be some kind of integer, but not a specific size.
+ * In addition to their use here, these functions are also called by the
+ * protocol-level pr_ctloutput() routines.
+ *
+ * Returns: 0 Success
+ * EINVAL
+ * copyin:EFAULT
+ */
+int
+sooptcopyin(struct sockopt *sopt, void *buf, size_t len, size_t minlen)
+{
+ size_t valsize;
+
+ /*
+ * If the user gives us more than we wanted, we ignore it,
+ * but if we don't get the minimum length the caller
+ * wants, we return EINVAL. On success, sopt->sopt_valsize
+ * is set to however much we actually retrieved.
+ */
+ if ((valsize = sopt->sopt_valsize) < minlen)
+ return (EINVAL);
+ if (valsize > len)
+ sopt->sopt_valsize = valsize = len;
+
+ if (sopt->sopt_p != kernproc)
+ return (copyin(sopt->sopt_val, buf, valsize));
+
+ bcopy(CAST_DOWN(caddr_t, sopt->sopt_val), buf, valsize);
+ return (0);
+}
+
+/*
+ * sooptcopyin_timeval
+ * Copy in a timeval value into tv_p, and take into account whether the
+ * the calling process is 64-bit or 32-bit. Moved the sanity checking
+ * code here so that we can verify the 64-bit tv_sec value before we lose
+ * the top 32-bits assigning tv64.tv_sec to tv_p->tv_sec.
+ */
+static int
+sooptcopyin_timeval(struct sockopt *sopt, struct timeval *tv_p)
+{
+ int error;
+
+ if (proc_is64bit(sopt->sopt_p)) {
+ struct user64_timeval tv64;
+
+ if (sopt->sopt_valsize < sizeof (tv64))
+ return (EINVAL);
+
+ sopt->sopt_valsize = sizeof (tv64);
+ if (sopt->sopt_p != kernproc) {
+ error = copyin(sopt->sopt_val, &tv64, sizeof (tv64));
+ if (error != 0)
+ return (error);
+ } else {
+ bcopy(CAST_DOWN(caddr_t, sopt->sopt_val), &tv64,
+ sizeof (tv64));
+ }
+ if (tv64.tv_sec < 0 || tv64.tv_sec > LONG_MAX ||
+ tv64.tv_usec < 0 || tv64.tv_usec >= 1000000)
+ return (EDOM);
+
+ tv_p->tv_sec = tv64.tv_sec;
+ tv_p->tv_usec = tv64.tv_usec;
+ } else {
+ struct user32_timeval tv32;
+
+ if (sopt->sopt_valsize < sizeof (tv32))
+ return (EINVAL);
+
+ sopt->sopt_valsize = sizeof (tv32);
+ if (sopt->sopt_p != kernproc) {
+ error = copyin(sopt->sopt_val, &tv32, sizeof (tv32));
+ if (error != 0) {
+ return (error);
+ }
+ } else {
+ bcopy(CAST_DOWN(caddr_t, sopt->sopt_val), &tv32,
+ sizeof (tv32));
+ }
+#ifndef __LP64__
+ /*
+ * K64todo "comparison is always false due to
+ * limited range of data type"
+ */
+ if (tv32.tv_sec < 0 || tv32.tv_sec > LONG_MAX ||
+ tv32.tv_usec < 0 || tv32.tv_usec >= 1000000)
+ return (EDOM);
+#endif
+ tv_p->tv_sec = tv32.tv_sec;
+ tv_p->tv_usec = tv32.tv_usec;
+ }
+ return (0);
+}
+
+/*
+ * Returns: 0 Success
+ * EINVAL
+ * ENOPROTOOPT
+ * ENOBUFS
+ * EDOM
+ * sooptcopyin:EINVAL
+ * sooptcopyin:EFAULT
+ * sooptcopyin_timeval:EINVAL
+ * sooptcopyin_timeval:EFAULT
+ * sooptcopyin_timeval:EDOM
+ * <pr_ctloutput>:EOPNOTSUPP[AF_UNIX]
+ * <pr_ctloutput>:???w
+ * sflt_attach_private:??? [whatever a filter author chooses]
+ * <sf_setoption>:??? [whatever a filter author chooses]
+ *
+ * Notes: Other <pru_listen> returns depend on the protocol family; all
+ * <sf_listen> returns depend on what the filter author causes
+ * their filter to return.
+ */
+int
+sosetoptlock(struct socket *so, struct sockopt *sopt, int dolock)
+{
+ int error, optval;
+ struct linger l;
+ struct timeval tv;
+#if CONFIG_MACF_SOCKET
+ struct mac extmac;
+#endif /* MAC_SOCKET */
+
+ if (sopt->sopt_dir != SOPT_SET)
+ sopt->sopt_dir = SOPT_SET;
+
+ if (dolock)
+ socket_lock(so, 1);
+
+ if ((so->so_state & (SS_CANTRCVMORE | SS_CANTSENDMORE)) ==
+ (SS_CANTRCVMORE | SS_CANTSENDMORE) &&
+ (so->so_flags & SOF_NPX_SETOPTSHUT) == 0) {
+ /* the socket has been shutdown, no more sockopt's */
+ error = EINVAL;
+ goto out;
+ }
+
+ error = sflt_setsockopt(so, sopt);
+ if (error != 0) {
+ if (error == EJUSTRETURN)
+ error = 0;
+ goto out;
+ }
+
+ if (sopt->sopt_level != SOL_SOCKET) {
+ if (so->so_proto != NULL &&
+ so->so_proto->pr_ctloutput != NULL) {
+ error = (*so->so_proto->pr_ctloutput)(so, sopt);
+ goto out;
+ }
+ error = ENOPROTOOPT;
+ } else {
+ /*
+ * Allow socket-level (SOL_SOCKET) options to be filtered by
+ * the protocol layer, if needed. A zero value returned from
+ * the handler means use default socket-level processing as
+ * done by the rest of this routine. Otherwise, any other
+ * return value indicates that the option is unsupported.
+ */
+ if (so->so_proto != NULL && (error = so->so_proto->pr_usrreqs->
+ pru_socheckopt(so, sopt)) != 0)
+ goto out;
+
+ error = 0;
+ switch (sopt->sopt_name) {
+ case SO_LINGER:
+ case SO_LINGER_SEC:
+ error = sooptcopyin(sopt, &l, sizeof (l), sizeof (l));
+ if (error != 0)
+ goto out;
+
+ so->so_linger = (sopt->sopt_name == SO_LINGER) ?
+ l.l_linger : l.l_linger * hz;
+ if (l.l_onoff != 0)
+ so->so_options |= SO_LINGER;
+ else
+ so->so_options &= ~SO_LINGER;
+ break;
+
+ case SO_DEBUG:
+ case SO_KEEPALIVE:
+ case SO_DONTROUTE:
+ case SO_USELOOPBACK:
+ case SO_BROADCAST:
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+ case SO_OOBINLINE:
+ case SO_TIMESTAMP:
+ case SO_TIMESTAMP_MONOTONIC:
+ case SO_DONTTRUNC:
+ case SO_WANTMORE:
+ case SO_WANTOOBFLAG:
+ case SO_NOWAKEFROMSLEEP:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0)
+ goto out;
+ if (optval)
+ so->so_options |= sopt->sopt_name;
+ else
+ so->so_options &= ~sopt->sopt_name;
+ break;
+
+ case SO_SNDBUF:
+ case SO_RCVBUF:
+ case SO_SNDLOWAT:
+ case SO_RCVLOWAT:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0)
+ goto out;
+
+ /*
+ * Values < 1 make no sense for any of these
+ * options, so disallow them.
+ */
+ if (optval < 1) {
+ error = EINVAL;
+ goto out;
+ }
+
+ switch (sopt->sopt_name) {
+ case SO_SNDBUF:
+ case SO_RCVBUF: {
+ struct sockbuf *sb =
+ (sopt->sopt_name == SO_SNDBUF) ?
+ &so->so_snd : &so->so_rcv;
+ if (sbreserve(sb, (u_int32_t)optval) == 0) {
+ error = ENOBUFS;
+ goto out;
+ }
+ sb->sb_flags |= SB_USRSIZE;
+ sb->sb_flags &= ~SB_AUTOSIZE;
+ sb->sb_idealsize = (u_int32_t)optval;
+ break;
+ }
+ /*
+ * Make sure the low-water is never greater than
+ * the high-water.
+ */
+ case SO_SNDLOWAT: {
+ int space = sbspace(&so->so_snd);
+ u_int32_t hiwat = so->so_snd.sb_hiwat;
+
+ if (so->so_snd.sb_flags & SB_UNIX) {
+ struct unpcb *unp =
+ (struct unpcb *)(so->so_pcb);
+ if (unp != NULL && unp->unp_conn != NULL) {
+ hiwat += unp->unp_conn->unp_cc;
+ }
+ }
+
+ so->so_snd.sb_lowat =
+ (optval > hiwat) ?
+ hiwat : optval;
+
+ if (space >= so->so_snd.sb_lowat) {
+ sowwakeup(so);
+ }
+ break;
+ }
+ case SO_RCVLOWAT: {
+ int64_t data_len;
+ so->so_rcv.sb_lowat =
+ (optval > so->so_rcv.sb_hiwat) ?
+ so->so_rcv.sb_hiwat : optval;
+ data_len = so->so_rcv.sb_cc
+ - so->so_rcv.sb_ctl;
+ if (data_len >= so->so_rcv.sb_lowat)
+ sorwakeup(so);
+ break;
+ }
+ }
+ break;
+
+ case SO_SNDTIMEO:
+ case SO_RCVTIMEO:
+ error = sooptcopyin_timeval(sopt, &tv);
+ if (error != 0)
+ goto out;
+
+ switch (sopt->sopt_name) {
+ case SO_SNDTIMEO:
+ so->so_snd.sb_timeo = tv;
+ break;
+ case SO_RCVTIMEO:
+ so->so_rcv.sb_timeo = tv;
+ break;
+ }
+ break;
+
+ case SO_NKE: {
+ struct so_nke nke;
+
+ error = sooptcopyin(sopt, &nke, sizeof (nke),
+ sizeof (nke));
+ if (error != 0)
+ goto out;
+
+ error = sflt_attach_internal(so, nke.nke_handle);
+ break;
+ }
+
+ case SO_NOSIGPIPE:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0)
+ goto out;
+ if (optval != 0)
+ so->so_flags |= SOF_NOSIGPIPE;
+ else
+ so->so_flags &= ~SOF_NOSIGPIPE;
+ break;
+
+ case SO_NOADDRERR:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0)
+ goto out;
+ if (optval != 0)
+ so->so_flags |= SOF_NOADDRAVAIL;
+ else
+ so->so_flags &= ~SOF_NOADDRAVAIL;
+ break;
+
+ case SO_REUSESHAREUID:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0)
+ goto out;
+ if (optval != 0)
+ so->so_flags |= SOF_REUSESHAREUID;
+ else
+ so->so_flags &= ~SOF_REUSESHAREUID;
+ break;
+
+ case SO_NOTIFYCONFLICT:
+ if (kauth_cred_issuser(kauth_cred_get()) == 0) {
+ error = EPERM;
+ goto out;
+ }
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0)
+ goto out;
+ if (optval != 0)
+ so->so_flags |= SOF_NOTIFYCONFLICT;
+ else
+ so->so_flags &= ~SOF_NOTIFYCONFLICT;
+ break;
+
+ case SO_RESTRICTIONS:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0)
+ goto out;
+
+ error = so_set_restrictions(so, optval);
+ break;
+
+ case SO_AWDL_UNRESTRICTED:
+ if (SOCK_DOM(so) != PF_INET &&
+ SOCK_DOM(so) != PF_INET6) {
+ error = EOPNOTSUPP;
+ goto out;
+ }
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error != 0)
+ goto out;
+ if (optval != 0) {
+ kauth_cred_t cred = NULL;
+ proc_t ep = PROC_NULL;
+
+ if (so->so_flags & SOF_DELEGATED) {
+ ep = proc_find(so->e_pid);
+ if (ep)
+ cred = kauth_cred_proc_ref(ep);
+ }
+ error = priv_check_cred(
+ cred ? cred : so->so_cred,
+ PRIV_NET_RESTRICTED_AWDL, 0);
+ if (error == 0)
+ inp_set_awdl_unrestricted(
+ sotoinpcb(so));
+ if (cred)
+ kauth_cred_unref(&cred);
+ if (ep != PROC_NULL)
+ proc_rele(ep);
+ } else
+ inp_clear_awdl_unrestricted(sotoinpcb(so));
+ break;
+
+ case SO_LABEL:
+#if CONFIG_MACF_SOCKET
+ if ((error = sooptcopyin(sopt, &extmac, sizeof (extmac),
+ sizeof (extmac))) != 0)
+ goto out;
+
+ error = mac_setsockopt_label(proc_ucred(sopt->sopt_p),
+ so, &extmac);
+#else
+ error = EOPNOTSUPP;
+#endif /* MAC_SOCKET */
+ break;
+
+ case SO_UPCALLCLOSEWAIT:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0)
+ goto out;
+ if (optval != 0)
+ so->so_flags |= SOF_UPCALLCLOSEWAIT;
+ else
+ so->so_flags &= ~SOF_UPCALLCLOSEWAIT;
+ break;
+
+ case SO_RANDOMPORT:
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0)
+ goto out;
+ if (optval != 0)
+ 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 != 0)
+ goto out;
+ if (sonpx.npx_mask & ~SONPX_MASK_VALID) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * 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 != 0)
+ goto out;
+ error = so_set_traffic_class(so, optval);
+ if (error != 0)
+ goto out;
+ break;
+ }
+
+ case SO_RECV_TRAFFIC_CLASS: {
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0)
+ goto out;
+ 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 != 0)
+ goto out;
+ error = so_set_tcdbg(so, &so_tcdbg);
+ if (error != 0)
+ goto out;
+ break;
+ }
+
+ case SO_PRIVILEGED_TRAFFIC_CLASS:
+ error = priv_check_cred(kauth_cred_get(),
+ PRIV_NET_PRIVILEGED_TRAFFIC_CLASS, 0);
+ if (error != 0)
+ goto out;
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0)
+ goto out;
+ 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 out;
+ }
+ /*
+ * 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 out;
+ }
+ if (optval)
+ so->so_flags &= ~SOF_NODEFUNCT;
+ else
+ so->so_flags |= SOF_NODEFUNCT;
+
+ if (SOCK_DOM(so) == PF_INET ||
+ SOCK_DOM(so) == PF_INET6) {
+ char s[MAX_IPv6_STR_LEN];
+ char d[MAX_IPv6_STR_LEN];
+ struct inpcb *inp = sotoinpcb(so);
+
+ SODEFUNCTLOG(("%s[%d]: so 0x%llx [%s %s:%d -> "
+ "%s:%d] is now marked as %seligible for "
+ "defunct\n", __func__, proc_selfpid(),
+ (uint64_t)VM_KERNEL_ADDRPERM(so),
+ (SOCK_TYPE(so) == SOCK_STREAM) ?
+ "TCP" : "UDP", inet_ntop(SOCK_DOM(so),
+ ((SOCK_DOM(so) == PF_INET) ?
+ (void *)&inp->inp_laddr.s_addr :
+ (void *)&inp->in6p_laddr), s, sizeof (s)),
+ ntohs(inp->in6p_lport),
+ inet_ntop(SOCK_DOM(so),
+ (SOCK_DOM(so) == PF_INET) ?
+ (void *)&inp->inp_faddr.s_addr :
+ (void *)&inp->in6p_faddr, d, sizeof (d)),
+ ntohs(inp->in6p_fport),
+ (so->so_flags & SOF_NODEFUNCT) ?
+ "not " : ""));
+ } else {
+ SODEFUNCTLOG(("%s[%d]: so 0x%llx [%d,%d] is "
+ "now marked as %seligible for defunct\n",
+ __func__, proc_selfpid(),
+ (uint64_t)VM_KERNEL_ADDRPERM(so),
+ SOCK_DOM(so), SOCK_TYPE(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;
+
+ case SO_TRAFFIC_MGT_BACKGROUND: {
+ /* This option is handled by lower layer(s) */
+ error = 0;
+ break;
+ }
+
+#if FLOW_DIVERT
+ case SO_FLOW_DIVERT_TOKEN:
+ error = flow_divert_token_set(so, sopt);
+ break;
+#endif /* FLOW_DIVERT */
+
+
+ case SO_DELEGATED:
+ if ((error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval))) != 0)
+ break;
+
+ error = so_set_effective_pid(so, optval, sopt->sopt_p);
+ break;
+
+ case SO_DELEGATED_UUID: {
+ uuid_t euuid;
+
+ if ((error = sooptcopyin(sopt, &euuid, sizeof (euuid),
+ sizeof (euuid))) != 0)
+ break;
+
+ error = so_set_effective_uuid(so, euuid, sopt->sopt_p);
+ break;
+ }
+
+#if NECP
+ case SO_NECP_ATTRIBUTES:
+ error = necp_set_socket_attributes(so, sopt);
+ break;
+#endif /* NECP */
+
+#if MPTCP
+ case SO_MPTCP_FASTJOIN:
+ if (!((so->so_flags & SOF_MP_SUBFLOW) ||
+ ((SOCK_CHECK_DOM(so, PF_MULTIPATH)) &&
+ (SOCK_CHECK_PROTO(so, IPPROTO_TCP))))) {
+ error = ENOPROTOOPT;
+ break;
+ }
+
+ error = sooptcopyin(sopt, &optval, sizeof (optval),
+ sizeof (optval));
+ if (error != 0)
+ goto out;
+ if (optval == 0)
+ so->so_flags &= ~SOF_MPTCP_FASTJOIN;
+ else
+ so->so_flags |= SOF_MPTCP_FASTJOIN;
+ break;
+#endif /* MPTCP */
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ if (error == 0 && so->so_proto != NULL &&
+ so->so_proto->pr_ctloutput != NULL) {
+ (void) so->so_proto->pr_ctloutput(so, sopt);
+ }
+ }
+out:
+ if (dolock)
+ 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.
+ */
+ valsize = min(len, sopt->sopt_valsize);
+ sopt->sopt_valsize = valsize;
+ if (sopt->sopt_val != USER_ADDR_NULL) {
+ if (sopt->sopt_p != kernproc)
+ error = copyout(buf, sopt->sopt_val, valsize);
+ else
+ bcopy(buf, CAST_DOWN(caddr_t, sopt->sopt_val), valsize);
+ }
+ return (error);
+}
+
+static int
+sooptcopyout_timeval(struct sockopt *sopt, const struct timeval *tv_p)
+{
+ int error;
+ size_t len;
+ struct user64_timeval tv64;
+ struct user32_timeval tv32;
+ const void * val;
+ size_t valsize;
+
+ error = 0;
+ if (proc_is64bit(sopt->sopt_p)) {
+ len = sizeof (tv64);
+ tv64.tv_sec = tv_p->tv_sec;
+ tv64.tv_usec = tv_p->tv_usec;
+ val = &tv64;
+ } else {
+ len = sizeof (tv32);
+ tv32.tv_sec = tv_p->tv_sec;
+ tv32.tv_usec = tv_p->tv_usec;
+ val = &tv32;
+ }
+ valsize = min(len, sopt->sopt_valsize);
+ sopt->sopt_valsize = valsize;
+ if (sopt->sopt_val != USER_ADDR_NULL) {
+ if (sopt->sopt_p != kernproc)
+ error = copyout(val, sopt->sopt_val, valsize);
+ else
+ bcopy(val, CAST_DOWN(caddr_t, sopt->sopt_val), valsize);
+ }
+ return (error);
+}
+
+/*
+ * Return: 0 Success
+ * ENOPROTOOPT
+ * <pr_ctloutput>:EOPNOTSUPP[AF_UNIX]
+ * <pr_ctloutput>:???
+ * <sf_getoption>:???
+ */
+int
+sogetoptlock(struct socket *so, struct sockopt *sopt, int dolock)
+{
+ int error, optval;
+ struct linger l;
+ struct timeval tv;
+#if CONFIG_MACF_SOCKET
+ struct mac extmac;
+#endif /* MAC_SOCKET */
+
+ if (sopt->sopt_dir != SOPT_GET)
+ sopt->sopt_dir = SOPT_GET;
+
+ if (dolock)
+ socket_lock(so, 1);
+
+ error = sflt_getsockopt(so, sopt);
+ if (error != 0) {
+ if (error == EJUSTRETURN)
+ error = 0;
+ goto out;
+ }
+
+ if (sopt->sopt_level != SOL_SOCKET) {
+ if (so->so_proto != NULL &&
+ so->so_proto->pr_ctloutput != NULL) {
+ error = (*so->so_proto->pr_ctloutput)(so, sopt);
+ goto out;
+ }
+ error = ENOPROTOOPT;
+ } else {
+ /*
+ * Allow socket-level (SOL_SOCKET) options to be filtered by
+ * the protocol layer, if needed. A zero value returned from
+ * the handler means use default socket-level processing as
+ * done by the rest of this routine. Otherwise, any other
+ * return value indicates that the option is unsupported.
+ */
+ if (so->so_proto != NULL && (error = so->so_proto->pr_usrreqs->
+ pru_socheckopt(so, sopt)) != 0)
+ goto out;
+
+ error = 0;
+ switch (sopt->sopt_name) {
+ case SO_LINGER:
+ case SO_LINGER_SEC:
+ l.l_onoff = ((so->so_options & SO_LINGER) ? 1 : 0);
+ l.l_linger = (sopt->sopt_name == SO_LINGER) ?
+ so->so_linger : so->so_linger / hz;
+ error = sooptcopyout(sopt, &l, sizeof (l));
+ break;
+
+ case SO_USELOOPBACK:
+ case SO_DONTROUTE:
+ case SO_DEBUG:
+ case SO_KEEPALIVE:
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+ case SO_BROADCAST:
+ case SO_OOBINLINE:
+ case SO_TIMESTAMP:
+ case SO_TIMESTAMP_MONOTONIC:
+ case SO_DONTTRUNC:
+ case SO_WANTMORE:
+ case SO_WANTOOBFLAG:
+ case SO_NOWAKEFROMSLEEP:
+ optval = so->so_options & sopt->sopt_name;
+integer:
+ error = sooptcopyout(sopt, &optval, sizeof (optval));
+ break;
+
+ case SO_TYPE:
+ optval = so->so_type;
+ goto integer;
+
+ case SO_NREAD:
+ if (so->so_proto->pr_flags & PR_ATOMIC) {
+ int pkt_total;
+ struct mbuf *m1;
+
+ pkt_total = 0;
+ m1 = so->so_rcv.sb_mb;
+ while (m1 != NULL) {
+ if (m1->m_type == MT_DATA ||
+ m1->m_type == MT_HEADER ||
+ m1->m_type == MT_OOBDATA)
+ pkt_total += m1->m_len;
+ m1 = m1->m_next;
+ }
+ optval = pkt_total;
+ } else {
+ optval = so->so_rcv.sb_cc - so->so_rcv.sb_ctl;
+ }
+ goto integer;
+
+ case SO_NUMRCVPKT:
+ if (so->so_proto->pr_flags & PR_ATOMIC) {
+ int cnt = 0;
+ struct mbuf *m1;
+
+ m1 = so->so_rcv.sb_mb;
+ while (m1 != NULL) {
+ if (m1->m_type == MT_DATA ||
+ m1->m_type == MT_HEADER ||
+ m1->m_type == MT_OOBDATA)
+ cnt += 1;
+ m1 = m1->m_nextpkt;
+ }
+ optval = cnt;
+ goto integer;
+ } else {
+ error = EINVAL;
+ break;
+ }
+
+ case SO_NWRITE:
+ optval = so->so_snd.sb_cc;
+ goto integer;
+
+ case SO_ERROR:
+ optval = so->so_error;
+ so->so_error = 0;
+ goto integer;
+
+ case SO_SNDBUF: {
+ u_int32_t hiwat = so->so_snd.sb_hiwat;
+
+ if (so->so_snd.sb_flags & SB_UNIX) {
+ struct unpcb *unp =
+ (struct unpcb *)(so->so_pcb);
+ if (unp != NULL && unp->unp_conn != NULL) {
+ hiwat += unp->unp_conn->unp_cc;
+ }
+ }
+
+ optval = hiwat;
+ goto integer;
+ }
+ case SO_RCVBUF:
+ optval = so->so_rcv.sb_hiwat;
+ goto integer;
+
+ case SO_SNDLOWAT:
+ optval = so->so_snd.sb_lowat;
+ goto integer;
+
+ case SO_RCVLOWAT:
+ optval = so->so_rcv.sb_lowat;
+ goto integer;
+
+ case SO_SNDTIMEO:
+ case SO_RCVTIMEO:
+ tv = (sopt->sopt_name == SO_SNDTIMEO ?
+ so->so_snd.sb_timeo : so->so_rcv.sb_timeo);
+
+ error = sooptcopyout_timeval(sopt, &tv);
+ break;
+
+ case SO_NOSIGPIPE:
+ optval = (so->so_flags & SOF_NOSIGPIPE);
+ goto integer;
+
+ case SO_NOADDRERR:
+ optval = (so->so_flags & SOF_NOADDRAVAIL);
+ goto integer;
+
+ case SO_REUSESHAREUID:
+ optval = (so->so_flags & SOF_REUSESHAREUID);
+ goto integer;
+
+
+ case SO_NOTIFYCONFLICT:
+ optval = (so->so_flags & SOF_NOTIFYCONFLICT);
+ goto integer;
+
+ case SO_RESTRICTIONS:
+ optval = so_get_restrictions(so);
+ goto integer;
+
+ case SO_AWDL_UNRESTRICTED:
+ if (SOCK_DOM(so) == PF_INET ||
+ SOCK_DOM(so) == PF_INET6) {
+ optval = inp_get_awdl_unrestricted(
+ sotoinpcb(so));
+ goto integer;
+ } else
+ error = EOPNOTSUPP;
+ break;
+
+ case SO_LABEL:
+#if CONFIG_MACF_SOCKET
+ if ((error = sooptcopyin(sopt, &extmac, sizeof (extmac),
+ sizeof (extmac))) != 0 ||
+ (error = mac_socket_label_get(proc_ucred(
+ sopt->sopt_p), so, &extmac)) != 0)
+ break;
+
+ error = sooptcopyout(sopt, &extmac, sizeof (extmac));
+#else
+ error = EOPNOTSUPP;
+#endif /* MAC_SOCKET */
+ break;
+
+ case SO_PEERLABEL:
+#if CONFIG_MACF_SOCKET
+ if ((error = sooptcopyin(sopt, &extmac, sizeof (extmac),
+ sizeof (extmac))) != 0 ||
+ (error = mac_socketpeer_label_get(proc_ucred(
+ sopt->sopt_p), so, &extmac)) != 0)
+ break;
+
+ error = sooptcopyout(sopt, &extmac, sizeof (extmac));
+#else
+ error = EOPNOTSUPP;
+#endif /* MAC_SOCKET */
+ break;
+
+#ifdef __APPLE_API_PRIVATE
+ case SO_UPCALLCLOSEWAIT:
+ optval = (so->so_flags & SOF_UPCALLCLOSEWAIT);
+ goto integer;
+#endif
+ case SO_RANDOMPORT:
+ optval = (so->so_flags & SOF_BINDRANDOMPORT);
+ goto integer;
+
+ case SO_NP_EXTENSIONS: {
+ struct so_np_extensions sonpx;
+
+ sonpx.npx_flags = (so->so_flags & SOF_NPX_SETOPTSHUT) ?
+ SONPX_SETOPTSHUT : 0;
+ sonpx.npx_mask = SONPX_MASK_VALID;
+
+ error = sooptcopyout(sopt, &sonpx,
+ sizeof (struct so_np_extensions));
+ break;
+ }
+
+ case SO_TRAFFIC_CLASS:
+ optval = so->so_traffic_class;
+ goto integer;
+
+ case SO_RECV_TRAFFIC_CLASS:
+ optval = (so->so_flags & SOF_RECV_TRAFFIC_CLASS);
+ goto integer;
+
+ case SO_TRAFFIC_CLASS_STATS:
+ error = sooptcopyout(sopt, &so->so_tc_stats,
+ sizeof (so->so_tc_stats));
+ break;
+
+ case SO_TRAFFIC_CLASS_DBG:
+ error = sogetopt_tcdbg(so, sopt);
+ break;
+
+ case SO_PRIVILEGED_TRAFFIC_CLASS:
+ optval = (so->so_flags & SOF_PRIVILEGED_TRAFFIC_CLASS);
+ goto integer;
+
+ case SO_DEFUNCTOK:
+ optval = !(so->so_flags & SOF_NODEFUNCT);
+ goto integer;
+
+ case SO_ISDEFUNCT:
+ optval = (so->so_flags & SOF_DEFUNCT);
+ goto integer;
+
+ case SO_OPPORTUNISTIC:
+ optval = so_get_opportunistic(so);
+ goto integer;
+
+ case SO_FLUSH:
+ /* This option is not gettable */
+ error = EINVAL;
+ break;
+
+ case SO_RECV_ANYIF:
+ optval = so_get_recv_anyif(so);
+ goto integer;
+
+ case SO_TRAFFIC_MGT_BACKGROUND:
+ /* This option is handled by lower layer(s) */
+ if (so->so_proto != NULL &&
+ so->so_proto->pr_ctloutput != NULL) {
+ (void) so->so_proto->pr_ctloutput(so, sopt);
+ }
+ break;
+
+#if FLOW_DIVERT
+ case SO_FLOW_DIVERT_TOKEN:
+ error = flow_divert_token_get(so, sopt);
+ break;
+#endif /* FLOW_DIVERT */
+
+#if NECP
+ case SO_NECP_ATTRIBUTES:
+ error = necp_get_socket_attributes(so, sopt);
+ break;
+#endif /* NECP */
+
+#if CONTENT_FILTER
+ case SO_CFIL_SOCK_ID: {
+ cfil_sock_id_t sock_id;
+
+ sock_id = cfil_sock_id_from_socket(so);
+
+ error = sooptcopyout(sopt, &sock_id,
+ sizeof(cfil_sock_id_t));
+ break;
+ }
+#endif /* CONTENT_FILTER */
+
+#if MPTCP
+ case SO_MPTCP_FASTJOIN:
+ if (!((so->so_flags & SOF_MP_SUBFLOW) ||
+ ((SOCK_CHECK_DOM(so, PF_MULTIPATH)) &&
+ (SOCK_CHECK_PROTO(so, IPPROTO_TCP))))) {
+ error = ENOPROTOOPT;
+ break;
+ }
+ optval = (so->so_flags & SOF_MPTCP_FASTJOIN);
+ break;
+#endif /* MPTCP */
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ }
+out:
+ if (dolock)
+ socket_unlock(so, 1);
+ return (error);
+}
+
+/*
+ * The size limits on our soopt_getm is different from that on FreeBSD.
+ * We limit the size of options to MCLBYTES. This will have to change
+ * if we need to define options that need more space than MCLBYTES.
+ */
+int
+soopt_getm(struct sockopt *sopt, struct mbuf **mp)
+{
+ struct mbuf *m, *m_prev;
+ int sopt_size = sopt->sopt_valsize;
+ int how;
+
+ if (sopt_size <= 0 || sopt_size > MCLBYTES)
+ return (EMSGSIZE);
+
+ how = sopt->sopt_p != kernproc ? M_WAIT : M_DONTWAIT;
+ MGET(m, how, MT_DATA);
+ if (m == NULL)
+ return (ENOBUFS);
+ if (sopt_size > MLEN) {
+ MCLGET(m, how);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ return (ENOBUFS);
+ }
+ m->m_len = min(MCLBYTES, sopt_size);
+ } else {
+ m->m_len = min(MLEN, sopt_size);
+ }
+ sopt_size -= m->m_len;
+ *mp = m;
+ m_prev = m;
+
+ while (sopt_size > 0) {
+ MGET(m, how, MT_DATA);
+ if (m == NULL) {
+ m_freem(*mp);
+ return (ENOBUFS);
+ }
+ if (sopt_size > MLEN) {
+ MCLGET(m, how);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_freem(*mp);
+ m_freem(m);
+ return (ENOBUFS);
+ }
+ m->m_len = min(MCLBYTES, sopt_size);
+ } else {
+ m->m_len = min(MLEN, sopt_size);
+ }
+ sopt_size -= m->m_len;
+ m_prev->m_next = m;
+ m_prev = m;
+ }
+ return (0);
+}
+
+/* copyin sopt data into mbuf chain */
+int
+soopt_mcopyin(struct sockopt *sopt, struct mbuf *m)
+{
+ struct mbuf *m0 = m;
+
+ if (sopt->sopt_val == USER_ADDR_NULL)
+ return (0);
+ while (m != NULL && sopt->sopt_valsize >= m->m_len) {
+ if (sopt->sopt_p != kernproc) {
+ int error;
+
+ error = copyin(sopt->sopt_val, mtod(m, char *),
+ m->m_len);
+ if (error != 0) {
+ m_freem(m0);
+ return (error);
+ }
+ } else {
+ bcopy(CAST_DOWN(caddr_t, sopt->sopt_val),
+ mtod(m, char *), m->m_len);
+ }
+ sopt->sopt_valsize -= m->m_len;
+ sopt->sopt_val += m->m_len;
+ m = m->m_next;
+ }
+ /* should be allocated enoughly at ip6_sooptmcopyin() */
+ if (m != NULL) {
+ panic("soopt_mcopyin");
+ /* NOTREACHED */
+ }
+ return (0);
+}
+
+/* copyout mbuf chain data into soopt */
+int
+soopt_mcopyout(struct sockopt *sopt, struct mbuf *m)
+{
+ struct mbuf *m0 = m;
+ size_t valsize = 0;
+
+ if (sopt->sopt_val == USER_ADDR_NULL)
+ return (0);
+ while (m != NULL && sopt->sopt_valsize >= m->m_len) {
+ if (sopt->sopt_p != kernproc) {
+ int error;
+
+ error = copyout(mtod(m, char *), sopt->sopt_val,
+ m->m_len);
+ if (error != 0) {
+ m_freem(m0);
+ return (error);
+ }
+ } else {
+ bcopy(mtod(m, char *),
+ CAST_DOWN(caddr_t, sopt->sopt_val), m->m_len);
+ }
+ sopt->sopt_valsize -= m->m_len;
+ sopt->sopt_val += m->m_len;
+ valsize += m->m_len;
+ m = m->m_next;
+ }
+ if (m != NULL) {
+ /* enough soopt buffer should be given from user-land */
+ m_freem(m0);
+ return (EINVAL);
+ }
+ sopt->sopt_valsize = valsize;
+ return (0);
+}
+
+void
+sohasoutofband(struct socket *so)
+{
+ if (so->so_pgid < 0)
+ gsignal(-so->so_pgid, SIGURG);
+ else if (so->so_pgid > 0)
+ proc_signal(so->so_pgid, SIGURG);
+ selwakeup(&so->so_rcv.sb_sel);
+}
+
+int
+sopoll(struct socket *so, int events, kauth_cred_t cred, void * wql)
+{
+#pragma unused(cred)
+ struct proc *p = current_proc();
+ int revents = 0;
+
+ socket_lock(so, 1);
+ so_update_last_owner_locked(so, PROC_NULL);
+ so_update_policy(so);
+
+ if (events & (POLLIN | POLLRDNORM))
+ if (soreadable(so))
+ revents |= events & (POLLIN | POLLRDNORM);
+
+ if (events & (POLLOUT | POLLWRNORM))
+ if (sowriteable(so))
+ revents |= events & (POLLOUT | POLLWRNORM);
+
+ if (events & (POLLPRI | POLLRDBAND))
+ if (so->so_oobmark || (so->so_state & SS_RCVATMARK))
+ revents |= events & (POLLPRI | POLLRDBAND);
+
+ if (revents == 0) {
+ if (events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) {
+ /*
+ * Darwin sets the flag first,
+ * BSD calls selrecord first
+ */
+ so->so_rcv.sb_flags |= SB_SEL;
+ selrecord(p, &so->so_rcv.sb_sel, wql);
+ }
+
+ if (events & (POLLOUT | POLLWRNORM)) {
+ /*
+ * Darwin sets the flag first,
+ * BSD calls selrecord first
+ */
+ so->so_snd.sb_flags |= SB_SEL;
+ selrecord(p, &so->so_snd.sb_sel, wql);
+ }
+ }