/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #define _KSOCKET_MAGIC 0xabcdef09 #define KSOCKET_VALID(ks) (ks->kso_magic == _KSOCKET_MAGIC) #define KSTOSO(ks) (ks->kso_fd) #ifndef SS_CLOSING #define SS_CLOSING 0x00010000 #endif /* * NB: you can't cast this into a sonode like you can with a normal * ksocket_t, but no correct code should ever do that anyway. * The ksocket_t type is opaque to prevent exactly that. */ struct __ksocket { uint32_t kso_magic; uint32_t kso_count; uint32_t kso_state; int kso_fd; kmutex_t kso_lock; kcondvar_t kso_closing_cv; }; static umem_cache_t *ksocket_cache = NULL; /*ARGSUSED*/ static int _ksocket_ctor(void *buf, void *arg, int flags) { ksocket_t sock = buf; bzero(sock, sizeof (*sock)); mutex_init(&sock->kso_lock, NULL, MUTEX_DEFAULT, NULL); cv_init(&sock->kso_closing_cv, NULL, CV_DEFAULT, NULL); return (0); } /*ARGSUSED*/ static void _ksocket_dtor(void *buf, void *arg) { ksocket_t sock = buf; mutex_destroy(&sock->kso_lock); cv_destroy(&sock->kso_closing_cv); } #pragma init(_ksocket_init) int _ksocket_init(void) { ksocket_cache = umem_cache_create("ksocket", sizeof (struct __ksocket), 0, _ksocket_ctor, _ksocket_dtor, NULL, NULL, NULL, 0); VERIFY(ksocket_cache != NULL); return (0); } #pragma fini(_ksocket_fini) int _ksocket_fini(void) { umem_cache_destroy(ksocket_cache); return (0); } static ksocket_t _ksocket_create(int fd) { ksocket_t ks; ks = umem_cache_alloc(ksocket_cache, 0); VERIFY(ks != NULL); ks->kso_magic = _KSOCKET_MAGIC; ks->kso_count = 1; ks->kso_fd = fd; return (ks); } static void _ksocket_destroy(ksocket_t ks) { ASSERT(ks->kso_count == 1); umem_cache_free(ksocket_cache, ks); } int ksocket_socket(ksocket_t *ksp, int domain, int type, int protocol, int flags, struct cred *cr) { int fd; ksocket_t ks; /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); ASSERT(flags == KSOCKET_SLEEP || flags == KSOCKET_NOSLEEP); fd = socket(domain, type, protocol); if (fd < 0) { *ksp = NULL; return (errno); } ks = _ksocket_create(fd); *ksp = ks; return (0); } /* * This is marked NODIRECT so the main program linking with this library * can provide its own "bind helper" function. See: fksmbd_ksock.c */ /* ARGSUSED */ int ksocket_bind_helper(int fd, struct sockaddr *addr, uint_t addrlen) { return (EACCES); } int ksocket_bind(ksocket_t ks, struct sockaddr *addr, socklen_t addrlen, struct cred *cr) { int err = 0; /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) return (ENOTSOCK); if (bind(KSTOSO(ks), addr, addrlen) != 0) err = errno; if (err == EACCES) { err = ksocket_bind_helper(KSTOSO(ks), addr, addrlen); } return (err); } int ksocket_listen(ksocket_t ks, int backlog, struct cred *cr) { /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) return (ENOTSOCK); if (listen(KSTOSO(ks), backlog) != 0) return (errno); return (0); } int ksocket_accept(ksocket_t ks, struct sockaddr *addr, socklen_t *addrlenp, ksocket_t *nks, struct cred *cr) { int fd; /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); *nks = NULL; if (!KSOCKET_VALID(ks)) return (ENOTSOCK); if (addr != NULL && addrlenp == NULL) return (EFAULT); fd = accept(KSTOSO(ks), addr, addrlenp); if (fd < 0) return (errno); *nks = _ksocket_create(fd); return (0); } int ksocket_connect(ksocket_t ks, struct sockaddr *addr, socklen_t addrlen, struct cred *cr) { /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) return (ENOTSOCK); if (connect(KSTOSO(ks), addr, addrlen) != 0) return (errno); return (0); } int ksocket_send(ksocket_t ks, void *msg, size_t msglen, int flags, size_t *sent, struct cred *cr) { ssize_t error; /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) { if (sent != NULL) *sent = 0; return (ENOTSOCK); } error = send(KSTOSO(ks), msg, msglen, flags); if (error < 0) { if (sent != NULL) *sent = 0; return (errno); } if (sent != NULL) *sent = (size_t)error; return (0); } int ksocket_sendto(ksocket_t ks, void *msg, size_t msglen, int flags, struct sockaddr *name, socklen_t namelen, size_t *sent, struct cred *cr) { ssize_t error; /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) { if (sent != NULL) *sent = 0; return (ENOTSOCK); } error = sendto(KSTOSO(ks), msg, msglen, flags, name, namelen); if (error < 0) { if (sent != NULL) *sent = 0; return (errno); } if (sent != NULL) *sent = (size_t)error; return (0); } int ksocket_sendmsg(ksocket_t ks, struct nmsghdr *msg, int flags, size_t *sent, struct cred *cr) { uio_t uio; ssize_t len; /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) { if (sent != NULL) *sent = 0; return (ENOTSOCK); } /* socksyscalls.c uses MSG_MAXIOVLEN (local macro), both are 16 */ ASSERT3U(msg->msg_iovlen, <=, IOV_MAX); len = sendmsg(KSTOSO(ks), msg, flags); if (len < 0) { if (sent != NULL) *sent = 0; return (errno); } /* * The user-level sendmsg() does NOT update msg->iov like * ksocket_sendmsg(). It's unclear whether that's a bug * or if that was intentional. Anyway, update it here. */ if (msg->msg_iov != NULL) { bzero(&uio, sizeof (uio)); uio.uio_iov = msg->msg_iov; uio.uio_iovcnt = msg->msg_iovlen; uio.uio_resid = len; uioskip(&uio, len); ASSERT(uio.uio_resid == 0); msg->msg_iov = uio.uio_iov; msg->msg_iovlen = uio.uio_iovcnt; } if (sent != NULL) *sent = (size_t)len; return (0); } int ksocket_recv(ksocket_t ks, void *msg, size_t msglen, int flags, size_t *recvd, struct cred *cr) { ssize_t error; /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) { if (recvd != NULL) *recvd = 0; return (ENOTSOCK); } error = recv(KSTOSO(ks), msg, msglen, flags); if (error < 0) { if (recvd != NULL) *recvd = 0; return (errno); } if (recvd != NULL) *recvd = (size_t)error; return (0); } int ksocket_recvfrom(ksocket_t ks, void *msg, size_t msglen, int flags, struct sockaddr *name, socklen_t *namelen, size_t *recvd, struct cred *cr) { ssize_t error; /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) { if (recvd != NULL) *recvd = 0; return (ENOTSOCK); } error = recvfrom(KSTOSO(ks), msg, msglen, flags, name, namelen); if (error != 0) { if (recvd != NULL) *recvd = 0; return (errno); } if (recvd != NULL) *recvd = (ssize_t)error; return (0); } int ksocket_recvmsg(ksocket_t ks, struct nmsghdr *msg, int flags, size_t *recvd, struct cred *cr) { ssize_t error; /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) { if (recvd != NULL) *recvd = 0; return (ENOTSOCK); } error = recvmsg(KSTOSO(ks), msg, flags); if (error < 0) { if (recvd != NULL) *recvd = 0; return (errno); } if (recvd != NULL) *recvd = (size_t)error; return (0); } int ksocket_shutdown(ksocket_t ks, int how, struct cred *cr) { /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) return (ENOTSOCK); if (shutdown(KSTOSO(ks), how) != 0) return (errno); return (0); } int ksocket_close(ksocket_t ks, struct cred *cr) { int fd; /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); mutex_enter(&ks->kso_lock); if (!KSOCKET_VALID(ks)) { mutex_exit(&ks->kso_lock); return (ENOTSOCK); } ks->kso_state |= SS_CLOSING; /* * The real ksocket wakes up everything. * It seems the only way we can do that * is to go ahead and close the FD. */ fd = ks->kso_fd; ks->kso_fd = -1; (void) close(fd); while (ks->kso_count > 1) cv_wait(&ks->kso_closing_cv, &ks->kso_lock); mutex_exit(&ks->kso_lock); _ksocket_destroy(ks); return (0); } int ksocket_getsockname(ksocket_t ks, struct sockaddr *addr, socklen_t *addrlen, struct cred *cr) { /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) return (ENOTSOCK); if (addrlen == NULL || (addr == NULL && *addrlen != 0)) return (EFAULT); if (getsockname(KSTOSO(ks), addr, addrlen) != 0) return (errno); return (0); } int ksocket_getpeername(ksocket_t ks, struct sockaddr *addr, socklen_t *addrlen, struct cred *cr) { /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) return (ENOTSOCK); if (addrlen == NULL || (addr == NULL && *addrlen != 0)) return (EFAULT); if (getpeername(KSTOSO(ks), addr, addrlen) != 0) return (errno); return (0); } int ksocket_setsockopt(ksocket_t ks, int level, int optname, const void *optval, int optlen, struct cred *cr) { /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) return (ENOTSOCK); if (optval == NULL) optlen = 0; if (setsockopt(KSTOSO(ks), level, optname, optval, optlen) != 0) return (errno); return (0); } int ksocket_ioctl(ksocket_t ks, int cmd, intptr_t arg, int *rvp, struct cred *cr) { int rval; /* All Solaris components should pass a cred for this operation. */ ASSERT(cr != NULL); if (!KSOCKET_VALID(ks)) return (ENOTSOCK); rval = ioctl(KSTOSO(ks), cmd, arg); if (rvp != NULL) *rvp = rval; if (rval != 0) rval = errno; return (rval); } void ksocket_hold(ksocket_t ks) { if (!mutex_owned(&ks->kso_lock)) { mutex_enter(&ks->kso_lock); ks->kso_count++; mutex_exit(&ks->kso_lock); } else ks->kso_count++; } void ksocket_rele(ksocket_t ks) { /* * When so_count equals 1 means no thread working on this ksocket */ VERIFY3U(ks->kso_count, >, 1); if (!mutex_owned(&ks->kso_lock)) { mutex_enter(&ks->kso_lock); if (--ks->kso_count == 1) cv_signal(&ks->kso_closing_cv); mutex_exit(&ks->kso_lock); } else { if (--ks->kso_count == 1) cv_signal(&ks->kso_closing_cv); } }