#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/debug.h>
#include <sys/errno.h>
#include <sys/stropts.h>
#include <sys/cmn_err.h>
#include <sys/sysmacros.h>
#include <sys/policy.h>
#include <sys/filio.h>
#include <sys/sockio.h>
#include <sys/project.h>
#include <sys/tihdr.h>
#include <sys/strsubr.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/strsun.h>
#include <sys/tsol/label.h>
#include <inet/sdp_itf.h>
#include "socksdp.h"
#include <fs/sockfs/sockcommon.h>
static int sosdp_init(struct sonode *, struct sonode *, struct cred *, int);
static int sosdp_accept(struct sonode *, int, struct cred *, struct sonode **);
static int sosdp_bind(struct sonode *, struct sockaddr *, socklen_t, int,
struct cred *);
static int sosdp_listen(struct sonode *, int, struct cred *);
static int sosdp_connect(struct sonode *, struct sockaddr *, socklen_t,
int, int, struct cred *);
static int sosdp_recvmsg(struct sonode *, struct nmsghdr *, struct uio *,
struct cred *);
static int sosdp_sendmsg(struct sonode *, struct nmsghdr *, struct uio *,
struct cred *);
static int sosdp_getpeername(struct sonode *, struct sockaddr *, socklen_t *,
boolean_t, struct cred *);
static int sosdp_getsockname(struct sonode *, struct sockaddr *, socklen_t *,
struct cred *);
static int sosdp_shutdown(struct sonode *, int, struct cred *);
static int sosdp_getsockopt(struct sonode *, int, int, void *, socklen_t *,
int, struct cred *);
static int sosdp_setsockopt(struct sonode *, int, int, const void *,
socklen_t, struct cred *);
static int sosdp_ioctl(struct sonode *, int, intptr_t, int, struct cred *,
int32_t *);
static int sosdp_poll(struct sonode *, short, int, short *,
struct pollhead **);
static int sosdp_close(struct sonode *, int, struct cred *);
void sosdp_fini(struct sonode *, struct cred *);
static void *sdp_sock_newconn(void *parenthandle, void *connind);
static void sdp_sock_connected(void *handle);
static void sdp_sock_disconnected(void *handle, int error);
static void sdp_sock_connfail(void *handle, int error);
static int sdp_sock_recv(void *handle, mblk_t *mp, int flags);
static void sdp_sock_xmitted(void *handle, int txqueued);
static void sdp_sock_urgdata(void *handle);
static void sdp_sock_ordrel(void *handle);
sonodeops_t sosdp_sonodeops = {
sosdp_init,
sosdp_accept,
sosdp_bind,
sosdp_listen,
sosdp_connect,
sosdp_recvmsg,
sosdp_sendmsg,
so_sendmblk_notsupp,
sosdp_getpeername,
sosdp_getsockname,
sosdp_shutdown,
sosdp_getsockopt,
sosdp_setsockopt,
sosdp_ioctl,
sosdp_poll,
sosdp_close,
};
sdp_upcalls_t sosdp_sock_upcalls = {
sdp_sock_newconn,
sdp_sock_connected,
sdp_sock_disconnected,
sdp_sock_connfail,
sdp_sock_recv,
sdp_sock_xmitted,
sdp_sock_urgdata,
sdp_sock_ordrel,
};
static int
sosdp_init(struct sonode *so, struct sonode *pso, struct cred *cr, int flags)
{
int error = 0;
sdp_sockbuf_limits_t sbl;
sdp_upcalls_t *upcalls;
if (pso != NULL) {
mutex_enter(&so->so_lock);
so->so_state |= (SS_ISBOUND | SS_ISCONNECTED |
(pso->so_state & SS_ASYNC));
sosdp_so_inherit(pso, so);
so->so_proto_props = pso->so_proto_props;
mutex_exit(&so->so_lock);
return (0);
}
if ((error = secpolicy_basic_net_access(cr)) != 0)
return (error);
upcalls = &sosdp_sock_upcalls;
so->so_proto_handle = (sock_lower_handle_t)sdp_create(so, NULL,
so->so_family, SDP_CAN_BLOCK, upcalls, &sbl, cr, &error);
if (so->so_proto_handle == NULL)
return (ENOMEM);
so->so_rcvbuf = sbl.sbl_rxbuf;
so->so_rcvlowat = sbl.sbl_rxlowat;
so->so_sndbuf = sbl.sbl_txbuf;
so->so_sndlowat = sbl.sbl_txlowat;
return (error);
}
static int
sosdp_accept(struct sonode *lso, int fflag, struct cred *cr,
struct sonode **nsop)
{
int error = 0;
struct sonode *nso;
dprint(3, ("sosdp_accept: so:%p so_proto_handle:%p", (void *)lso,
(void *)lso->so_proto_handle));
if (!(lso->so_state & SS_ACCEPTCONN)) {
eprintsoline(lso, EINVAL);
return (EINVAL);
}
error = so_acceptq_dequeue(lso, (fflag & (FNONBLOCK|FNDELAY)), &nso);
if (error != 0) {
eprintsoline(lso, error);
dprint(4, ("sosdp_accept: failed %d:lso:%p so_proto_handle:%p",
error, (void *)lso, (void *)lso->so_proto_handle));
return (error);
}
dprint(2, ("sosdp_accept: new %p\n", (void *)nso));
*nsop = nso;
return (0);
}
int
sosdp_bind(struct sonode *so, struct sockaddr *name, socklen_t namelen,
int flags, struct cred *cr)
{
int error = 0;
if (!(flags & _SOBIND_LOCK_HELD)) {
mutex_enter(&so->so_lock);
so_lock_single(so);
} else {
ASSERT(MUTEX_HELD(&so->so_lock));
ASSERT(so->so_flag & SOLOCKED);
}
if ((so->so_state & SS_ISBOUND) || name == NULL || namelen == 0) {
error = EINVAL;
eprintsoline(so, error);
goto done;
}
if (so->so_state & SS_CANTSENDMORE) {
error = EINVAL;
goto done;
}
mutex_exit(&so->so_lock);
error = sdp_bind((struct sdp_conn_struct_t *)so->so_proto_handle,
name, namelen);
mutex_enter(&so->so_lock);
if (error == 0) {
so->so_state |= SS_ISBOUND;
} else {
eprintsoline(so, error);
}
done:
if (!(flags & _SOBIND_LOCK_HELD)) {
so_unlock_single(so, SOLOCKED);
mutex_exit(&so->so_lock);
} else {
ASSERT(MUTEX_HELD(&so->so_lock));
ASSERT(so->so_flag & SOLOCKED);
}
return (error);
}
static int
sosdp_listen(struct sonode *so, int backlog, struct cred *cr)
{
int error = 0;
mutex_enter(&so->so_lock);
so_lock_single(so);
if (so->so_state & (SS_ISCONNECTING | SS_ISCONNECTED |
SS_ISDISCONNECTING | SS_CANTRCVMORE | SS_CANTSENDMORE)) {
error = EINVAL;
eprintsoline(so, EINVAL);
goto done;
}
if (so->so_state & SS_ACCEPTCONN) {
so->so_backlog = backlog;
goto done;
}
mutex_exit(&so->so_lock);
error = sdp_listen((struct sdp_conn_struct_t *)so->so_proto_handle,
backlog);
mutex_enter(&so->so_lock);
if (error == 0) {
so->so_state |= (SS_ACCEPTCONN | SS_ISBOUND);
so->so_backlog = backlog;
} else {
eprintsoline(so, error);
}
done:
so_unlock_single(so, SOLOCKED);
mutex_exit(&so->so_lock);
return (error);
}
static int
sosdp_connect(struct sonode *so, struct sockaddr *name,
socklen_t namelen, int fflag, int flags, struct cred *cr)
{
int error = 0;
mutex_enter(&so->so_lock);
so_lock_single(so);
if (so->so_state & (SS_ACCEPTCONN|SS_ISCONNECTED|SS_ISCONNECTING)) {
if (so->so_state & SS_ISCONNECTED) {
error = EISCONN;
} else if (so->so_state & SS_ISCONNECTING) {
error = EALREADY;
} else {
error = EOPNOTSUPP;
}
eprintsoline(so, error);
goto done;
}
if (so->so_error != 0) {
error = sogeterr(so, B_TRUE);
eprintsoline(so, error);
goto done;
}
if (so->so_state & (SS_ISDISCONNECTING|SS_CANTRCVMORE|
SS_CANTSENDMORE)) {
error = EINVAL;
eprintsoline(so, error);
goto done;
}
if (name == NULL || namelen == 0) {
eprintsoline(so, EINVAL);
goto done;
}
soisconnecting(so);
mutex_exit(&so->so_lock);
error = sdp_connect((struct sdp_conn_struct_t *)so->so_proto_handle,
name, namelen);
mutex_enter(&so->so_lock);
if (error == 0) {
error = sowaitconnected(so, fflag, 0);
dprint(4,
("sosdp_connect: wait on so:%p "
"so_proto_handle:%p failed:%d",
(void *)so, (void *)so->so_proto_handle, error));
}
switch (error) {
case 0:
case EINPROGRESS:
case EALREADY:
case EINTR:
so->so_state |= SS_ISBOUND;
break;
default:
so->so_state &= ~SS_ISCONNECTING;
break;
}
done:
so_unlock_single(so, SOLOCKED);
mutex_exit(&so->so_lock);
return (error);
}
int
sosdp_recvmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
struct cred *cr)
{
int flags, error = 0;
int size;
flags = msg->msg_flags;
msg->msg_flags = 0;
if (!(so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING|
SS_CANTRCVMORE))) {
return (ENOTCONN);
}
mutex_enter(&so->so_lock);
error = so_lock_read_intr(so,
uiop->uio_fmode | ((flags & MSG_DONTWAIT) ? FNONBLOCK : 0));
if (error != 0) {
mutex_exit(&so->so_lock);
return (error);
}
size = uiop->uio_resid;
mutex_exit(&so->so_lock);
if (!(so->so_state & SS_CANTRCVMORE)) {
if (uiop->uio_fmode & (FNDELAY|FNONBLOCK)) {
flags |= MSG_DONTWAIT;
}
error = sdp_recv(
(struct sdp_conn_struct_t *)so->so_proto_handle, msg,
size, flags, uiop);
} else {
msg->msg_controllen = 0;
msg->msg_namelen = 0;
}
mutex_enter(&so->so_lock);
so_unlock_read(so);
mutex_exit(&so->so_lock);
return (error);
}
static int
sosdp_sendmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
struct cred *cr)
{
int flags;
ssize_t count;
int error;
ASSERT(so->so_type == SOCK_STREAM);
dprint(4, ("sosdp_sendmsg: so:%p so_proto_handle:%p",
(void *)so, (void *)so->so_proto_handle));
flags = msg->msg_flags;
if (msg->msg_controllen != 0) {
return (EOPNOTSUPP);
}
mutex_enter(&so->so_lock);
if (so->so_state & SS_CANTSENDMORE) {
mutex_exit(&so->so_lock);
return (EPIPE);
}
if (so->so_error != 0) {
error = sogeterr(so, B_TRUE);
mutex_exit(&so->so_lock);
return (error);
}
if (uiop->uio_fmode & (FNDELAY|FNONBLOCK))
flags |= MSG_DONTWAIT;
count = uiop->uio_resid;
if (!(so->so_state & (SS_ISCONNECTING | SS_ISCONNECTED))) {
dprint(4, ("sosdp_sendmsg: invalid state: <%x>",
so->so_state));
mutex_exit(&so->so_lock);
return (ENOTCONN);
}
mutex_exit(&so->so_lock);
error = sdp_send((struct sdp_conn_struct_t *)so->so_proto_handle,
msg, count, flags, uiop);
return (error);
}
static int
sosdp_getpeername(struct sonode *so, struct sockaddr *addr, socklen_t *addrlen,
boolean_t accept, struct cred *cr)
{
if (!accept && !(so->so_state & SS_ISCONNECTED)) {
return (ENOTCONN);
} else {
return (sdp_getpeername(
(struct sdp_conn_struct_t *)so->so_proto_handle,
addr, addrlen));
}
}
static int
sosdp_getsockname(struct sonode *so, struct sockaddr *addr, socklen_t *addrlen,
struct cred *cr)
{
mutex_enter(&so->so_lock);
if (!(so->so_state & SS_ISBOUND)) {
if (so->so_family == AF_INET || so->so_family == AF_INET6) {
bzero(addr, *addrlen);
*addrlen = (so->so_family == AF_INET6) ?
sizeof (struct sockaddr_in6) :
sizeof (struct sockaddr_in);
addr->sa_family = so->so_family;
}
mutex_exit(&so->so_lock);
return (0);
} else {
mutex_exit(&so->so_lock);
return (sdp_getsockname(
(struct sdp_conn_struct_t *)so->so_proto_handle,
addr, addrlen));
}
}
static int
sosdp_shutdown(struct sonode *so, int how, struct cred *cr)
{
uint_t state_change;
int error = 0;
mutex_enter(&so->so_lock);
so_lock_single(so);
state_change = so->so_state;
if (!(state_change & SS_ISCONNECTED)) {
error = ENOTCONN;
goto done;
}
switch (how) {
case SHUT_RD:
socantrcvmore(so);
break;
case SHUT_WR:
socantsendmore(so);
break;
case SHUT_RDWR:
socantsendmore(so);
socantrcvmore(so);
break;
default:
error = EINVAL;
goto done;
}
state_change = so->so_state & ~state_change;
if (state_change & SS_CANTSENDMORE) {
so->so_state |= SS_ISDISCONNECTING;
}
so_notify_shutdown(so);
if (state_change & SS_CANTSENDMORE) {
error = sdp_shutdown(
(struct sdp_conn_struct_t *)so->so_proto_handle, how);
}
mutex_enter(&so->so_lock);
done:
so_unlock_single(so, SOLOCKED);
mutex_exit(&so->so_lock);
if (error == EWOULDBLOCK)
error = 0;
return (error);
}
static int
sosdp_getsockopt(struct sonode *so, int level, int option_name,
void *optval, socklen_t *optlenp, int flags, struct cred *cr)
{
int error = 0;
void *option = NULL;
socklen_t maxlen = *optlenp, len, optlen;
uint32_t value;
uint8_t buffer[4];
void *optbuf = &buffer;
mutex_enter(&so->so_lock);
if (level == SOL_SOCKET) {
switch (option_name) {
case SO_TYPE:
case SO_ERROR:
case SO_DEBUG:
case SO_ACCEPTCONN:
case SO_REUSEADDR:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_USELOOPBACK:
case SO_OOBINLINE:
case SO_SNDBUF:
case SO_RCVBUF:
case SO_SNDLOWAT:
case SO_RCVLOWAT:
case SO_DGRAM_ERRIND:
if (maxlen < (t_uscalar_t)sizeof (int32_t)) {
error = EINVAL;
eprintsoline(so, error);
goto done;
}
break;
case SO_LINGER:
if (maxlen < (t_uscalar_t)sizeof (struct linger)) {
error = EINVAL;
eprintsoline(so, error);
goto done;
}
break;
}
len = (t_uscalar_t)sizeof (uint32_t);
option = &value;
switch (option_name) {
case SO_TYPE:
value = so->so_type;
goto copyout;
case SO_ERROR:
value = sogeterr(so, B_TRUE);
goto copyout;
case SO_ACCEPTCONN:
value = (so->so_state & SS_ACCEPTCONN) ?
SO_ACCEPTCONN : 0;
goto copyout;
case SO_DEBUG:
case SO_REUSEADDR:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_USELOOPBACK:
case SO_OOBINLINE:
case SO_DGRAM_ERRIND:
value = (so->so_options & option_name);
goto copyout;
case SO_LINGER:
option = &so->so_linger;
len = (t_uscalar_t)sizeof (struct linger);
break;
case SO_SNDBUF:
value = so->so_sndbuf;
len = (t_uscalar_t)sizeof (int);
goto copyout;
case SO_RCVBUF:
value = so->so_rcvbuf;
len = (t_uscalar_t)sizeof (int);
goto copyout;
case SO_SNDLOWAT:
value = so->so_sndlowat;
len = (t_uscalar_t)sizeof (int);
goto copyout;
case SO_RCVLOWAT:
value = so->so_rcvlowat;
len = (t_uscalar_t)sizeof (int);
goto copyout;
default:
option = NULL;
break;
}
}
if (maxlen > sizeof (buffer)) {
optbuf = kmem_alloc(maxlen, KM_SLEEP);
}
optlen = maxlen;
mutex_exit(&so->so_lock);
error = sdp_get_opt((struct sdp_conn_struct_t *)so->so_proto_handle,
level, option_name, optbuf, &optlen);
mutex_enter(&so->so_lock);
ASSERT(optlen <= maxlen);
if (error != 0) {
if (option == NULL) {
eprintsoline(so, error);
goto free;
}
error = 0;
goto copyout;
}
option = optbuf;
len = optlen;
copyout:
len = MIN(len, maxlen);
bcopy(option, optval, len);
*optlenp = len;
free:
if (optbuf != &buffer) {
kmem_free(optbuf, maxlen);
}
done:
mutex_exit(&so->so_lock);
return (error);
}
static int
sosdp_setsockopt(struct sonode *so, int level, int option_name,
const void *optval, t_uscalar_t optlen, struct cred *cr)
{
void *conn = NULL;
int error = 0;
if (so->so_state & SS_CANTSENDMORE) {
return (EINVAL);
}
mutex_enter(&so->so_lock);
so_lock_single(so);
if (so->so_type == SOCK_STREAM) {
conn = (void *)so->so_proto_handle;
}
dprint(2, ("sosdp_setsockopt (%d) - conn %p %d %d \n",
so->so_type, conn, level, option_name));
if (conn != NULL) {
mutex_exit(&so->so_lock);
error = sdp_set_opt((struct sdp_conn_struct_t *)conn, level,
option_name, optval, optlen);
mutex_enter(&so->so_lock);
}
if (level == SOL_SOCKET) {
boolean_t handled = B_FALSE;
switch (option_name) {
case SO_DEBUG:
case SO_REUSEADDR:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_USELOOPBACK:
case SO_OOBINLINE:
case SO_SNDBUF:
case SO_RCVBUF:
case SO_SNDLOWAT:
case SO_RCVLOWAT:
case SO_DGRAM_ERRIND:
if (optlen != (t_uscalar_t)sizeof (int32_t)) {
error = EINVAL;
eprintsoline(so, error);
goto done;
}
ASSERT(optval);
handled = B_TRUE;
break;
case SO_LINGER:
if (optlen != (t_uscalar_t)sizeof (struct linger)) {
error = EINVAL;
eprintsoline(so, error);
goto done;
}
ASSERT(optval);
handled = B_TRUE;
break;
}
#define intvalue (*(int32_t *)optval)
switch (option_name) {
case SO_TYPE:
case SO_ERROR:
case SO_ACCEPTCONN:
error = ENOPROTOOPT;
goto done;
case SO_LINGER: {
struct linger *l = (struct linger *)optval;
so->so_linger.l_linger = l->l_linger;
if (l->l_onoff) {
so->so_linger.l_onoff = SO_LINGER;
so->so_options |= SO_LINGER;
} else {
so->so_linger.l_onoff = 0;
so->so_options &= ~SO_LINGER;
}
break;
}
case SO_DEBUG:
case SO_REUSEADDR:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_USELOOPBACK:
case SO_OOBINLINE:
case SO_DGRAM_ERRIND:
if (intvalue != 0) {
dprintso(so, 1,
("sosdp_setsockopt: setting 0x%x\n",
option_name));
so->so_options |= option_name;
} else {
dprintso(so, 1,
("sosdp_setsockopt: clearing 0x%x\n",
option_name));
so->so_options &= ~option_name;
}
break;
case SO_SNDBUF:
so->so_sndbuf = intvalue;
if (so->so_sndlowat > so->so_sndbuf) {
so->so_sndlowat = so->so_sndbuf;
}
break;
case SO_RCVBUF:
so->so_rcvbuf = intvalue;
if (so->so_rcvlowat > so->so_rcvbuf) {
so->so_rcvlowat = so->so_rcvbuf;
}
break;
case SO_SNDLOWAT:
if (so->so_sndlowat > so->so_sndbuf) {
so->so_sndlowat = so->so_sndbuf;
}
break;
case SO_RCVLOWAT:
if (so->so_rcvlowat > so->so_rcvbuf) {
so->so_rcvlowat = so->so_rcvbuf;
}
break;
}
#undef intvalue
if (error != 0) {
if ((error == ENOPROTOOPT || error == EPROTO ||
error == EINVAL) && handled) {
dprintso(so, 1,
("sosdp_setsockopt: ignoring error %d "
"for 0x%x\n", error, option_name));
error = 0;
}
}
}
done:
so_unlock_single(so, SOLOCKED);
mutex_exit(&so->so_lock);
return (error);
}
static int
sosdp_ioctl(struct sonode *so, int cmd, intptr_t arg, int mode,
struct cred *cr, int32_t *rvalp)
{
int32_t value;
int error, intval;
pid_t pid;
switch (cmd) {
case FIONBIO:
if (so_copyin((void *)arg, &value, sizeof (int32_t),
(mode & (int)FKIOCTL))) {
return (EFAULT);
}
mutex_enter(&so->so_lock);
if (value != 0) {
so->so_state |= SS_NDELAY;
} else {
so->so_state &= ~SS_NDELAY;
}
mutex_exit(&so->so_lock);
return (0);
case FIOASYNC:
if (so_copyin((void *)arg, &value, sizeof (int32_t),
(mode & (int)FKIOCTL))) {
return (EFAULT);
}
mutex_enter(&so->so_lock);
if (value) {
so->so_state |= SS_ASYNC;
} else {
so->so_state &= ~SS_ASYNC;
}
mutex_exit(&so->so_lock);
return (0);
case SIOCSPGRP:
case FIOSETOWN:
if (so_copyin((void *)arg, &pid, sizeof (pid_t),
(mode & (int)FKIOCTL))) {
return (EFAULT);
}
mutex_enter(&so->so_lock);
error = (pid != so->so_pgrp) ? socket_chgpgrp(so, pid) : 0;
mutex_exit(&so->so_lock);
return (error);
case SIOCGPGRP:
case FIOGETOWN:
if (so_copyout(&so->so_pgrp, (void *)arg,
sizeof (pid_t), (mode & (int)FKIOCTL)))
return (EFAULT);
return (0);
case SIOCATMARK:
intval = 0;
error = sdp_ioctl(
(struct sdp_conn_struct_t *)so->so_proto_handle, cmd,
&intval, cr);
if (so_copyout(&intval, (void *)arg, sizeof (int),
(mode & (int)FKIOCTL)))
return (EFAULT);
return (0);
case SIOCSENABLESDP: {
int32_t enable;
if (so_copyin((void *)arg, &enable, sizeof (int32_t),
mode & (int)FKIOCTL))
return (EFAULT);
error = sdp_ioctl(
(struct sdp_conn_struct_t *)so->so_proto_handle, cmd,
&enable, cr);
if (so_copyout(&enable, (void *)arg,
sizeof (int32_t), (mode & (int)FKIOCTL)))
return (EFAULT);
return (0);
}
case FIONREAD:
if (so->so_state & SS_ACCEPTCONN) {
intval = 0;
} else {
mutex_enter(&so->so_lock);
intval = sdp_polldata(
(struct sdp_conn_struct_t *)so->so_proto_handle,
SDP_READ);
mutex_exit(&so->so_lock);
}
if (so_copyout(&intval, (void *)arg, sizeof (intval),
(mode & (int)FKIOCTL)))
return (EFAULT);
return (0);
default:
return (EINVAL);
}
}
static int
sosdp_poll(struct sonode *so, short events, int anyyet, short *reventsp,
struct pollhead **phpp)
{
short origevents = events;
int so_state;
so_state = so->so_state;
ASSERT(so->so_version != SOV_STREAM);
if (!(so_state & SS_ISCONNECTED) && (so->so_type == SOCK_STREAM)) {
events &= ~(POLLOUT|POLLWRBAND);
}
if (so->so_error != 0 &&
((POLLIN|POLLRDNORM|POLLOUT) & origevents) != 0) {
*reventsp = (POLLIN|POLLRDNORM|POLLOUT) & origevents;
goto done;
}
*reventsp = 0;
if (so->so_type != SOCK_STREAM) {
goto done;
}
if (sdp_polldata((struct sdp_conn_struct_t *)so->so_proto_handle,
SDP_XMIT)) {
*reventsp |= POLLOUT & events;
}
if (sdp_polldata((struct sdp_conn_struct_t *)so->so_proto_handle,
SDP_READ)) {
*reventsp |= (POLLIN|POLLRDNORM) & events;
}
if ((so_state & SS_CANTRCVMORE) || (so->so_acceptq_len > 0)) {
*reventsp |= (POLLIN|POLLRDNORM) & events;
}
done:
if ((*reventsp == 0 && !anyyet) || (events & POLLET)) {
*phpp = &so->so_poll_list;
}
return (0);
}
static int
sosdp_close(struct sonode *so, int flag, struct cred *cr)
{
int error = 0;
mutex_enter(&so->so_lock);
so_lock_single(so);
soisdisconnected(so, 0);
mutex_exit(&so->so_lock);
error = sdp_disconnect((struct sdp_conn_struct_t *)so->so_proto_handle,
flag);
mutex_enter(&so->so_lock);
so_unlock_single(so, SOLOCKED);
so_notify_disconnected(so, B_FALSE, error);
return (error);
}
void
sosdp_fini(struct sonode *so, struct cred *cr)
{
dprint(3, ("sosdp_fini: so:%p so_proto_handle:%p", (void *)so,
(void *)so->so_proto_handle));
ASSERT(so->so_ops == &sosdp_sonodeops);
if (so->so_proto_handle != NULL)
sdp_close((struct sdp_conn_struct_t *)so->so_proto_handle);
so->so_proto_handle = NULL;
mutex_enter(&so->so_lock);
so_acceptq_flush(so, B_TRUE);
mutex_exit(&so->so_lock);
sonode_fini(so);
}
static void *
sdp_sock_newconn(void *parenthandle, void *connind)
{
struct sonode *lso = parenthandle;
struct sonode *nso;
int error;
ASSERT(lso->so_state & SS_ACCEPTCONN);
ASSERT(lso->so_proto_handle != NULL);
ASSERT(lso->so_type == SOCK_STREAM);
dprint(3, ("sosdp_newconn A: so:%p so_proto_handle:%p", (void *)lso,
(void *)lso->so_proto_handle));
if (lso->so_rcv_queued >= lso->so_backlog) {
return (NULL);
}
nso = socket_newconn(lso, connind, NULL, SOCKET_NOSLEEP, &error);
if (nso == NULL) {
eprintsoline(lso, error);
return (NULL);
}
dprint(2, ("sdp_stream_newconn: new %p\n", (void *)nso));
(void) so_acceptq_enqueue(lso, nso);
mutex_enter(&lso->so_lock);
so_notify_newconn(lso);
return (nso);
}
static void
sdp_sock_connected(void *handle)
{
struct sonode *so = handle;
ASSERT(so->so_type == SOCK_STREAM);
dprint(3, ("sosdp_connected C: so:%p so_proto_handle:%p", (void *)so,
(void *)so->so_proto_handle));
mutex_enter(&so->so_lock);
ASSERT(so->so_proto_handle);
ASSERT(!(so->so_state & SS_ACCEPTCONN));
soisconnected(so);
so_notify_connected(so);
}
static void
sdp_sock_disconnected(void *handle, int error)
{
struct sonode *so = handle;
ASSERT(so->so_type == SOCK_STREAM);
dprint(2, ("sosdp_disconnected C: so:%p so_proto_handle:%p error:%d",
(void *)so, (void *)so->so_proto_handle, error));
mutex_enter(&so->so_lock);
ASSERT(so->so_proto_handle != NULL);
soisdisconnected(so, error);
so_notify_disconnected(so, B_FALSE, error);
}
static int
sdp_sock_recv(void *handle, mblk_t *mp, int flags)
{
struct sonode *so = handle;
ASSERT(so->so_type == SOCK_STREAM);
mutex_enter(&so->so_lock);
so_notify_data(so, 0);
return (so->so_rcvbuf);
}
static void
sdp_sock_xmitted(void *handle, int writeable)
{
struct sonode *so = handle;
dprint(4, ("sosdp_sock_xmitted: so:%p so_proto_handle:%p txq:%d",
(void *)so, (void *)so->so_proto_handle, writeable));
mutex_enter(&so->so_lock);
ASSERT(so->so_proto_handle != NULL);
if (!writeable) {
so_notify_writable(so);
} else {
mutex_exit(&so->so_lock);
}
}
static void
sdp_sock_urgdata(void *handle)
{
struct sonode *so = handle;
ASSERT(so->so_type == SOCK_STREAM);
mutex_enter(&so->so_lock);
ASSERT(so->so_proto_handle != NULL);
so_notify_oobsig(so);
}
static void
sdp_sock_ordrel(void *handle)
{
struct sonode *so = handle;
ASSERT(so->so_type == SOCK_STREAM);
dprint(4, ("sdp_sock_ordrel : so:%p, so_proto_handle:%p",
(void *)so, (void *)so->so_proto_handle));
mutex_enter(&so->so_lock);
socantrcvmore(so);
so_notify_eof(so);
}
static void
sdp_sock_connfail(void *handle, int error)
{
struct sonode *so = handle;
dprint(3, ("sosdp_conn Failed: so:%p so_proto_handle:%p", (void *)so,
(void *)so->so_proto_handle));
mutex_enter(&so->so_lock);
ASSERT(so->so_proto_handle != NULL);
so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING);
so->so_error = (ushort_t)error;
mutex_exit(&so->so_lock);
cv_broadcast(&so->so_state_cv);
}