+
+static inline void
+sock_set_tcp_stream_priority(socket_t sock)
+{
+ if ((SOCK_DOM(sock) == PF_INET || SOCK_DOM(sock) == PF_INET6) &&
+ SOCK_TYPE(sock) == SOCK_STREAM) {
+ set_tcp_stream_priority(sock);
+ }
+}
+
+/*
+ * Caller must have ensured socket is valid and won't be going away.
+ */
+void
+socket_set_traffic_mgt_flags_locked(socket_t sock, u_int8_t flags)
+{
+ u_int32_t soflags1 = 0;
+
+ if ((flags & TRAFFIC_MGT_SO_BACKGROUND))
+ soflags1 |= SOF1_TRAFFIC_MGT_SO_BACKGROUND;
+ if ((flags & TRAFFIC_MGT_TCP_RECVBG))
+ soflags1 |= SOF1_TRAFFIC_MGT_TCP_RECVBG;
+
+ (void) OSBitOrAtomic(soflags1, &sock->so_flags1);
+
+ sock_set_tcp_stream_priority(sock);
+}
+
+void
+socket_set_traffic_mgt_flags(socket_t sock, u_int8_t flags)
+{
+ socket_lock(sock, 1);
+ socket_set_traffic_mgt_flags_locked(sock, flags);
+ socket_unlock(sock, 1);
+}
+
+/*
+ * Caller must have ensured socket is valid and won't be going away.
+ */
+void
+socket_clear_traffic_mgt_flags_locked(socket_t sock, u_int8_t flags)
+{
+ u_int32_t soflags1 = 0;
+
+ if ((flags & TRAFFIC_MGT_SO_BACKGROUND))
+ soflags1 |= SOF1_TRAFFIC_MGT_SO_BACKGROUND;
+ if ((flags & TRAFFIC_MGT_TCP_RECVBG))
+ soflags1 |= SOF1_TRAFFIC_MGT_TCP_RECVBG;
+
+ (void) OSBitAndAtomic(~soflags1, &sock->so_flags1);
+
+ sock_set_tcp_stream_priority(sock);
+}
+
+void
+socket_clear_traffic_mgt_flags(socket_t sock, u_int8_t flags)
+{
+ socket_lock(sock, 1);
+ socket_clear_traffic_mgt_flags_locked(sock, flags);
+ socket_unlock(sock, 1);
+}
+
+
+/*
+ * Caller must have ensured socket is valid and won't be going away.
+ */
+errno_t
+socket_defunct(struct proc *p, socket_t so, int level)
+{
+ errno_t retval;
+
+ if (level != SHUTDOWN_SOCKET_LEVEL_DISCONNECT_SVC &&
+ level != SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL)
+ return (EINVAL);
+
+ socket_lock(so, 1);
+ /*
+ * SHUTDOWN_SOCKET_LEVEL_DISCONNECT_SVC level is meant to tear down
+ * all of mDNSResponder IPC sockets, currently those of AF_UNIX; note
+ * that this is an implementation artifact of mDNSResponder. We do
+ * a quick test against the socket buffers for SB_UNIX, since that
+ * would have been set by unp_attach() at socket creation time.
+ */
+ if (level == SHUTDOWN_SOCKET_LEVEL_DISCONNECT_SVC &&
+ (so->so_rcv.sb_flags & so->so_snd.sb_flags & SB_UNIX) != SB_UNIX) {
+ socket_unlock(so, 1);
+ return (EOPNOTSUPP);
+ }
+ retval = sosetdefunct(p, so, level, TRUE);
+ if (retval == 0)
+ retval = sodefunct(p, so, level);
+ socket_unlock(so, 1);
+ return (retval);
+}
+
+void
+sock_setupcalls_locked(socket_t sock, sock_upcall rcallback, void *rcontext,
+ sock_upcall wcallback, void *wcontext, int locked)
+{
+ if (rcallback != NULL) {
+ sock->so_rcv.sb_flags |= SB_UPCALL;
+ if (locked)
+ sock->so_rcv.sb_flags |= SB_UPCALL_LOCK;
+ sock->so_rcv.sb_upcall = rcallback;
+ sock->so_rcv.sb_upcallarg = rcontext;
+ } else {
+ sock->so_rcv.sb_flags &= ~(SB_UPCALL | SB_UPCALL_LOCK);
+ sock->so_rcv.sb_upcall = NULL;
+ sock->so_rcv.sb_upcallarg = NULL;
+ }
+
+ if (wcallback != NULL) {
+ sock->so_snd.sb_flags |= SB_UPCALL;
+ if (locked)
+ sock->so_snd.sb_flags |= SB_UPCALL_LOCK;
+ sock->so_snd.sb_upcall = wcallback;
+ sock->so_snd.sb_upcallarg = wcontext;
+ } else {
+ sock->so_snd.sb_flags &= ~(SB_UPCALL | SB_UPCALL_LOCK);
+ sock->so_snd.sb_upcall = NULL;
+ sock->so_snd.sb_upcallarg = NULL;
+ }
+}
+
+errno_t
+sock_setupcall(socket_t sock, sock_upcall callback, void *context)
+{
+ if (sock == NULL)
+ return (EINVAL);
+
+ /*
+ * Note that we don't wait for any in progress upcall to complete.
+ * On embedded, sock_setupcall() causes both read and write
+ * callbacks to be set; on desktop, only read callback is set
+ * to maintain legacy KPI behavior.
+ *
+ * The newer sock_setupcalls() KPI should be used instead to set
+ * the read and write callbacks and their respective parameters.
+ */
+ socket_lock(sock, 1);
+#if CONFIG_EMBEDDED
+ sock_setupcalls_locked(sock, callback, context, callback, context, 0);
+#else
+ sock_setupcalls_locked(sock, callback, context, NULL, NULL, 0);
+#endif /* !CONFIG_EMBEDDED */
+ socket_unlock(sock, 1);
+
+ return (0);
+}
+
+errno_t
+sock_setupcalls(socket_t sock, sock_upcall rcallback, void *rcontext,
+ sock_upcall wcallback, void *wcontext)
+{
+ if (sock == NULL)
+ return (EINVAL);
+
+ /*
+ * Note that we don't wait for any in progress upcall to complete.
+ */
+ socket_lock(sock, 1);
+ sock_setupcalls_locked(sock, rcallback, rcontext, wcallback, wcontext, 0);
+ socket_unlock(sock, 1);
+
+ return (0);
+}
+
+void
+sock_catchevents_locked(socket_t sock, sock_evupcall ecallback, void *econtext,
+ u_int32_t emask)
+{
+ socket_lock_assert_owned(sock);
+
+ /*
+ * Note that we don't wait for any in progress upcall to complete.
+ */
+ if (ecallback != NULL) {
+ sock->so_event = ecallback;
+ sock->so_eventarg = econtext;
+ sock->so_eventmask = emask;
+ } else {
+ sock->so_event = sonullevent;
+ sock->so_eventarg = NULL;
+ sock->so_eventmask = 0;
+ }
+}
+
+errno_t
+sock_catchevents(socket_t sock, sock_evupcall ecallback, void *econtext,
+ u_int32_t emask)
+{
+ if (sock == NULL)
+ return (EINVAL);
+
+ socket_lock(sock, 1);
+ sock_catchevents_locked(sock, ecallback, econtext, emask);
+ socket_unlock(sock, 1);
+
+ return (0);
+}
+
+/*
+ * Returns true whether or not a socket belongs to the kernel.
+ */
+int
+sock_iskernel(socket_t so)
+{
+ return (so && so->last_pid == 0);
+}