errno_t sock_accept(socket_t sock, struct sockaddr *from, int fromlen, int flags, sock_upcall callback, void *cookie, socket_t *new_sock) { struct sockaddr *sa; struct socket *new_so; lck_mtx_t *mutex_held; int dosocklock; errno_t error = 0; if (sock == NULL || new_sock == NULL) return (EINVAL); socket_lock(sock, 1); if ((sock->so_options & SO_ACCEPTCONN) == 0) { socket_unlock(sock, 1); return (EINVAL); } if ((flags & ~(MSG_DONTWAIT)) != 0) { socket_unlock(sock, 1); return (ENOTSUP); } if (((flags & MSG_DONTWAIT) != 0 || (sock->so_state & SS_NBIO) != 0) && sock->so_comp.tqh_first == NULL) { socket_unlock(sock, 1); return (EWOULDBLOCK); } if (sock->so_proto->pr_getlock != NULL) { mutex_held = (*sock->so_proto->pr_getlock)(sock, 0); dosocklock = 1; } else { mutex_held = sock->so_proto->pr_domain->dom_mtx; dosocklock = 0; } while (TAILQ_EMPTY(&sock->so_comp) && sock->so_error == 0) { if (sock->so_state & SS_CANTRCVMORE) { sock->so_error = ECONNABORTED; break; } error = msleep((caddr_t)&sock->so_timeo, mutex_held, PSOCK | PCATCH, "sock_accept", NULL); if (error != 0) { socket_unlock(sock, 1); return (error); } } if (sock->so_error != 0) { error = sock->so_error; sock->so_error = 0; socket_unlock(sock, 1); return (error); } new_so = TAILQ_FIRST(&sock->so_comp); TAILQ_REMOVE(&sock->so_comp, new_so, so_list); sock->so_qlen--; /* * Pass the pre-accepted socket to any interested socket filter(s). * Upon failure, the socket would have been closed by the callee. */ if (new_so->so_filt != NULL) { /* * Temporarily drop the listening socket's lock before we * hand off control over to the socket filter(s), but keep * a reference so that it won't go away. We'll grab it * again once we're done with the filter(s). */ socket_unlock(sock, 0); if ((error = soacceptfilter(new_so)) != 0) { /* Drop reference on listening socket */ sodereference(sock); return (error); } socket_lock(sock, 0); } if (dosocklock) { lck_mtx_assert(new_so->so_proto->pr_getlock(new_so, 0), LCK_MTX_ASSERT_NOTOWNED); socket_lock(new_so, 1); } new_so->so_state &= ~SS_COMP; new_so->so_head = NULL; (void) soacceptlock(new_so, &sa, 0); socket_unlock(sock, 1); /* release the head */ /* see comments in sock_setupcall() */ if (callback != NULL) { sock_setupcalls_common(new_so, callback, cookie, NULL, NULL); } if (sa != NULL && from != NULL) { if (fromlen > sa->sa_len) fromlen = sa->sa_len; memcpy(from, sa, fromlen); } if (sa != NULL) FREE(sa, M_SONAME); /* * If the socket has been marked as inactive by sosetdefunct(), * disallow further operations on it. */ if (new_so->so_flags & SOF_DEFUNCT) { (void) sodefunct(current_proc(), new_so, SHUTDOWN_SOCKET_LEVEL_DISCONNECT_INTERNAL); } *new_sock = new_so; if (dosocklock) socket_unlock(new_so, 1); return (error); }
static int shutdown_sockets_on_interface_proc_callout(proc_t p, void *arg) { struct filedesc *fdp; int i; struct ifnet *ifp = (struct ifnet *)arg; if (ifp == NULL) return (PROC_RETURNED); proc_fdlock(p); fdp = p->p_fd; for (i = 0; i < fdp->fd_nfiles; i++) { struct fileproc *fp = fdp->fd_ofiles[i]; struct fileglob *fg; struct socket *so; struct inpcb *inp; struct ifnet *inp_ifp; int error; if (fp == NULL || (fdp->fd_ofileflags[i] & UF_RESERVED) != 0) { continue; } fg = fp->f_fglob; if (FILEGLOB_DTYPE(fg) != DTYPE_SOCKET) continue; so = (struct socket *)fp->f_fglob->fg_data; if (SOCK_DOM(so) != PF_INET && SOCK_DOM(so) != PF_INET6) continue; inp = (struct inpcb *)so->so_pcb; if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) == WNT_STOPUSING) continue; socket_lock(so, 1); if (in_pcb_checkstate(inp, WNT_RELEASE, 1) == WNT_STOPUSING) { socket_unlock(so, 1); continue; } if (inp->inp_boundifp != NULL) { inp_ifp = inp->inp_boundifp; } else if (inp->inp_last_outifp != NULL) { inp_ifp = inp->inp_last_outifp; } else { socket_unlock(so, 1); continue; } if (inp_ifp != ifp && inp_ifp->if_delegated.ifp != ifp) { socket_unlock(so, 1); continue; } error = sosetdefunct(p, so, 0, TRUE); if (error != 0) { log(LOG_ERR, "%s: sosetdefunct() error %d", __func__, error); } else { error = sodefunct(p, so, 0); if (error != 0) { log(LOG_ERR, "%s: sodefunct() error %d", __func__, error); } } socket_unlock(so, 1); } proc_fdunlock(p); return (PROC_RETURNED); }